From d98c8c2515ce29aad1d944e6b61cab235e377a28 Mon Sep 17 00:00:00 2001 From: intuit-svc Date: Fri, 25 Oct 2024 18:11:37 -0700 Subject: [PATCH] Deploying docs for 0.10.0-next.4 --- next/404.html | 20 +++--- next/_astro/VersionSelector.DKtIbrZF.js | 1 + next/_astro/VersionSelector.Djy8rFe9.js | 1 - ...ss => dsl-content-playground.DxsvqU8g.css} | 2 +- ... => logo-dark-small.CNeORTxh_Z21YbU4.webp} | Bin next/assets/cross-platform/index.html | 30 ++++----- next/assets/custom/index.html | 60 ++++++++--------- next/assets/dsl/index.html | 28 ++++---- next/assets/index.html | 28 ++++---- next/assets/reference/index.html | 32 ++++----- next/assets/transforms/index.html | 32 ++++----- next/content/assets-views/index.html | 40 +++++------ next/content/data-expressions/index.html | 34 +++++----- next/content/index.html | 28 ++++---- next/content/navigation/index.html | 28 ++++---- next/content/schema/index.html | 36 +++++----- .../designing-semantic-assets/index.html | 30 ++++----- next/guides/formatting/index.html | 32 ++++----- next/guides/getting-started/index.html | 54 +++++++-------- next/guides/multi-flow-experiences/index.html | 32 ++++----- next/guides/plugin-implementation/index.html | 30 ++++----- next/guides/writing-plugins/index.html | 34 +++++----- next/index.html | 32 ++++----- next/player/about/index.html | 28 ++++---- next/player/faqs/index.html | 34 +++++----- next/player/team/index.html | 28 ++++---- next/plugins/android/coroutines/index.html | 40 +++++------ next/plugins/android/set-timeout/index.html | 28 ++++---- next/plugins/core/asset-transform/index.html | 32 ++++----- .../core/common-expressions/index.html | 34 +++++----- next/plugins/core/common-types/index.html | 34 +++++----- .../core/computed-properties/index.html | 32 ++++----- .../core/data-change-listener/index.html | 32 ++++----- next/plugins/core/data-filter/index.html | 30 ++++----- next/plugins/core/expression/index.html | 32 ++++----- next/plugins/core/markdown/index.html | 30 ++++----- next/plugins/core/meta/index.html | 30 ++++----- .../plugins/core/stage-revert-data/index.html | 30 ++++----- next/plugins/core/types-provider/index.html | 34 +++++----- next/plugins/index.html | 28 ++++---- .../external-action-view-modifier/index.html | 28 ++++---- .../swiftui-pending-transaction/index.html | 28 ++++---- next/plugins/ios/transition/index.html | 28 ++++---- .../multiplatform/asset-provider/index.html | 36 +++++----- .../multiplatform/async-node/index.html | 36 +++++----- next/plugins/multiplatform/beacon/index.html | 42 ++++++------ .../multiplatform/check-path/index.html | 49 +++++++------- .../multiplatform/console-logger/index.html | 30 ++++----- .../multiplatform/external-action/index.html | 36 +++++----- next/plugins/multiplatform/metrics/index.html | 38 +++++------ .../partial-match-fingerprint/index.html | 32 ++++----- next/plugins/multiplatform/pub-sub/index.html | 34 +++++----- .../multiplatform/shared-constants/index.html | 32 ++++----- .../react/auto-scroll-manager/index.html | 30 ++++----- ...ReactPlayer-mdx.c828b3f1.iframe.bundle.js} | 4 +- ...mdx.c828b3f1.iframe.bundle.js.LICENSE.txt} | 0 ... => Welcome-mdx.e70b16db.iframe.bundle.js} | 4 +- ...mdx.e70b16db.iframe.bundle.js.LICENSE.txt} | 0 next/storybook-demo/iframe.html | 2 +- next/storybook-demo/project.json | 2 +- ...sets-Action-mdx.e2cfd210.iframe.bundle.js} | 4 +- ...mdx.e2cfd210.iframe.bundle.js.LICENSE.txt} | 0 ...sets-Choice-mdx.c314d213.iframe.bundle.js} | 4 +- ...mdx.c314d213.iframe.bundle.js.LICENSE.txt} | 0 ...-Collection-mdx.75a0b339.iframe.bundle.js} | 4 +- ...mdx.75a0b339.iframe.bundle.js.LICENSE.txt} | 0 ...ssets-Image-mdx.ee40bafc.iframe.bundle.js} | 4 +- ...mdx.ee40bafc.iframe.bundle.js.LICENSE.txt} | 0 ...assets-Info-mdx.7f052c86.iframe.bundle.js} | 4 +- ...mdx.7f052c86.iframe.bundle.js.LICENSE.txt} | 0 ...ssets-Input-mdx.e84d4a9c.iframe.bundle.js} | 4 +- ...mdx.e84d4a9c.iframe.bundle.js.LICENSE.txt} | 0 ...ssets-Intro-mdx.a4ad2f60.iframe.bundle.js} | 4 +- ...mdx.a4ad2f60.iframe.bundle.js.LICENSE.txt} | 0 ...assets-Text-mdx.8e57eae0.iframe.bundle.js} | 4 +- ...mdx.8e57eae0.iframe.bundle.js.LICENSE.txt} | 0 ...=> runtime~main.024af743.iframe.bundle.js} | 2 +- next/tools/cli/index.html | 28 ++++---- next/tools/dsl-content-playground/index.html | 28 ++++---- next/tools/storybook/index.html | 62 +++++++++++++----- next/tools/view-ast-explorer/index.html | 28 ++++---- next/xlr/concepts/index.html | 28 ++++---- next/xlr/intro/index.html | 30 ++++----- next/xlr/usage/index.html | 62 +++++++++++++----- 84 files changed, 1013 insertions(+), 958 deletions(-) create mode 100644 next/_astro/VersionSelector.DKtIbrZF.js delete mode 100644 next/_astro/VersionSelector.Djy8rFe9.js rename next/_astro/{dsl-content-playground.DTr9yC1c.css => dsl-content-playground.DxsvqU8g.css} (82%) rename next/_astro/{logo-dark-small.CNeORTxh_1g9j8I.webp => logo-dark-small.CNeORTxh_Z21YbU4.webp} (100%) rename next/storybook-demo/{ReactPlayer-mdx.657a80aa.iframe.bundle.js => ReactPlayer-mdx.c828b3f1.iframe.bundle.js} (99%) rename next/storybook-demo/{ReactPlayer-mdx.657a80aa.iframe.bundle.js.LICENSE.txt => ReactPlayer-mdx.c828b3f1.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{Welcome-mdx.82ec2157.iframe.bundle.js => Welcome-mdx.e70b16db.iframe.bundle.js} (96%) rename next/storybook-demo/{Welcome-mdx.82ec2157.iframe.bundle.js.LICENSE.txt => Welcome-mdx.e70b16db.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Action-mdx.19f34173.iframe.bundle.js => reference-assets-Action-mdx.e2cfd210.iframe.bundle.js} (98%) rename next/storybook-demo/{reference-assets-Action-mdx.19f34173.iframe.bundle.js.LICENSE.txt => reference-assets-Action-mdx.e2cfd210.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Choice-mdx.5a0fe9b2.iframe.bundle.js => reference-assets-Choice-mdx.c314d213.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Choice-mdx.5a0fe9b2.iframe.bundle.js.LICENSE.txt => reference-assets-Choice-mdx.c314d213.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Collection-mdx.906bdc86.iframe.bundle.js => reference-assets-Collection-mdx.75a0b339.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Collection-mdx.906bdc86.iframe.bundle.js.LICENSE.txt => reference-assets-Collection-mdx.75a0b339.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Image-mdx.5ca7c29f.iframe.bundle.js => reference-assets-Image-mdx.ee40bafc.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Image-mdx.5ca7c29f.iframe.bundle.js.LICENSE.txt => reference-assets-Image-mdx.ee40bafc.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Info-mdx.caf90618.iframe.bundle.js => reference-assets-Info-mdx.7f052c86.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Info-mdx.caf90618.iframe.bundle.js.LICENSE.txt => reference-assets-Info-mdx.7f052c86.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Input-mdx.0e7df4b8.iframe.bundle.js => reference-assets-Input-mdx.e84d4a9c.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Input-mdx.0e7df4b8.iframe.bundle.js.LICENSE.txt => reference-assets-Input-mdx.e84d4a9c.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Intro-mdx.7e23d7b9.iframe.bundle.js => reference-assets-Intro-mdx.a4ad2f60.iframe.bundle.js} (96%) rename next/storybook-demo/{reference-assets-Intro-mdx.7e23d7b9.iframe.bundle.js.LICENSE.txt => reference-assets-Intro-mdx.a4ad2f60.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{reference-assets-Text-mdx.a09aa6ae.iframe.bundle.js => reference-assets-Text-mdx.8e57eae0.iframe.bundle.js} (97%) rename next/storybook-demo/{reference-assets-Text-mdx.a09aa6ae.iframe.bundle.js.LICENSE.txt => reference-assets-Text-mdx.8e57eae0.iframe.bundle.js.LICENSE.txt} (100%) rename next/storybook-demo/{runtime~main.f7e3880f.iframe.bundle.js => runtime~main.024af743.iframe.bundle.js} (93%) diff --git a/next/404.html b/next/404.html index 8df95d9e..d608638d 100644 --- a/next/404.html +++ b/next/404.html @@ -1,4 +1,4 @@ - 404 | Player - - Skip to content
Skip to content

404

Page not found. Check the URL or try using the search bar.
\ No newline at end of file +

404

Page not found. Check the URL or try using the search bar.
\ No newline at end of file diff --git a/next/_astro/VersionSelector.DKtIbrZF.js b/next/_astro/VersionSelector.DKtIbrZF.js new file mode 100644 index 00000000..e45a8d98 --- /dev/null +++ b/next/_astro/VersionSelector.DKtIbrZF.js @@ -0,0 +1 @@ +import{j as s}from"./jsx-runtime.s_qdhQ62.js";import{r}from"./index.DRjF_FHU.js";const i="https://player-ui.github.io/",c="next",p=()=>{const[o,a]=r.useState([]);return r.useEffect(()=>{(async()=>{const l=(await(await fetch("https://api.github.com/repos/player-ui/player-ui.github.io/contents/")).json()).filter(t=>t.type==="dir"&&t.name.match(/^\d/)).map(t=>({label:t.name,path:t.name}));a(l)})().catch(()=>{})},[]),o},m=o=>{const a=o.route??"",n=p();return s.jsxs("select",{"aria-label":"Select the version of the Player docs you with to see",value:c,style:{backgroundColor:"rgba(0, 0, 0, 0)"},onChange:e=>{console.error("test"),window.location.href=`${i}${e.target.value}/${a}`},children:[s.jsx("option",{value:"latest",children:"Latest"}),s.jsx("option",{value:"next",children:"Next"}),n.map(e=>s.jsx("option",{value:e.path,children:e.label},e.label))]})};export{m as default}; diff --git a/next/_astro/VersionSelector.Djy8rFe9.js b/next/_astro/VersionSelector.Djy8rFe9.js deleted file mode 100644 index 7fb95bdd..00000000 --- a/next/_astro/VersionSelector.Djy8rFe9.js +++ /dev/null @@ -1 +0,0 @@ -import{j as s}from"./jsx-runtime.s_qdhQ62.js";import{r}from"./index.DRjF_FHU.js";const i="https://player-ui.github.io/",c="next",p=()=>{const[o,a]=r.useState([]);return r.useEffect(()=>{(async()=>{const l=(await(await fetch("https://api.github.com/repos/player-ui/player-ui.github.io/contents/")).json()).filter(t=>t.type==="dir"&&t.name.match(/^\d/)).map(t=>({label:t.name,path:t.name}));a(l)})().catch(()=>{})},[]),o},m=o=>{const a=o.route,n=p();return s.jsxs("select",{"aria-label":"Select the version of the Player docs you with to see",value:c,style:{backgroundColor:"rgba(0, 0, 0, 0)"},onChange:e=>{console.error("test"),window.location.href=`${i}${e.target.value}/${a}`},children:[s.jsx("option",{value:"latest",children:"Latest"}),s.jsx("option",{value:"next",children:"Next"}),n.map(e=>s.jsx("option",{value:e.path,children:e.label},e.label))]})};export{m as default}; diff --git a/next/_astro/dsl-content-playground.DTr9yC1c.css b/next/_astro/dsl-content-playground.DxsvqU8g.css similarity index 82% rename from next/_astro/dsl-content-playground.DTr9yC1c.css rename to next/_astro/dsl-content-playground.DxsvqU8g.css index 73c3ef5b..51b07d68 100644 --- a/next/_astro/dsl-content-playground.DTr9yC1c.css +++ b/next/_astro/dsl-content-playground.DxsvqU8g.css @@ -1 +1 @@ -:root,::backdrop{--sl-color-white: hsl(0, 0%, 100%);--sl-color-gray-1: hsl(224, 20%, 94%);--sl-color-gray-2: hsl(224, 6%, 77%);--sl-color-gray-3: hsl(224, 6%, 56%);--sl-color-gray-4: hsl(224, 7%, 36%);--sl-color-gray-5: hsl(224, 10%, 23%);--sl-color-gray-6: hsl(224, 14%, 16%);--sl-color-black: hsl(224, 10%, 10%);--sl-hue-orange: 41;--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);--sl-hue-green: 101;--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);--sl-hue-blue: 234;--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);--sl-hue-purple: 281;--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);--sl-hue-red: 339;--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);--sl-color-accent-low: hsl(224, 54%, 20%);--sl-color-accent: hsl(224, 100%, 60%);--sl-color-accent-high: hsl(224, 100%, 85%);--sl-color-text: var(--sl-color-gray-2);--sl-color-text-accent: var(--sl-color-accent-high);--sl-color-text-invert: var(--sl-color-accent-low);--sl-color-bg: var(--sl-color-black);--sl-color-bg-nav: var(--sl-color-gray-6);--sl-color-bg-sidebar: var(--sl-color-gray-6);--sl-color-bg-inline-code: var(--sl-color-gray-5);--sl-color-bg-accent: var(--sl-color-accent-high);--sl-color-hairline-light: var(--sl-color-gray-5);--sl-color-hairline: var(--sl-color-gray-6);--sl-color-hairline-shade: var(--sl-color-black);--sl-color-backdrop-overlay: hsla(223, 13%, 10%, .66);--sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, .12), 0px 2px 1px hsla(0, 0%, 0%, .24);--sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, .08), 0px 5px 2px hsla(0, 0%, 0%, .08), 0px 3px 2px hsla(0, 0%, 0%, .12), 0px 1px 1px hsla(0, 0%, 0%, .15);--sl-shadow-lg: 0px 25px 7px hsla(0, 0%, 0%, .03), 0px 16px 6px hsla(0, 0%, 0%, .1), 0px 9px 5px hsla(223, 13%, 10%, .33), 0px 4px 4px hsla(0, 0%, 0%, .75), 0px 4px 2px hsla(0, 0%, 0%, .25);--sl-text-2xs: .75rem;--sl-text-xs: .8125rem;--sl-text-sm: .875rem;--sl-text-base: 1rem;--sl-text-lg: 1.125rem;--sl-text-xl: 1.25rem;--sl-text-2xl: 1.5rem;--sl-text-3xl: 1.8125rem;--sl-text-4xl: 2.1875rem;--sl-text-5xl: 2.625rem;--sl-text-6xl: 4rem;--sl-text-body: var(--sl-text-base);--sl-text-body-sm: var(--sl-text-xs);--sl-text-code: var(--sl-text-sm);--sl-text-code-sm: var(--sl-text-xs);--sl-text-h1: var(--sl-text-4xl);--sl-text-h2: var(--sl-text-3xl);--sl-text-h3: var(--sl-text-2xl);--sl-text-h4: var(--sl-text-xl);--sl-text-h5: var(--sl-text-lg);--sl-line-height: 1.75;--sl-line-height-headings: 1.2;--sl-font-system: 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";--sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);--__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), var(--sl-font-system-mono);--sl-nav-height: 3.5rem;--sl-nav-pad-x: 1rem;--sl-nav-pad-y: .75rem;--sl-mobile-toc-height: 3rem;--sl-sidebar-width: 18.75rem;--sl-sidebar-pad-x: 1rem;--sl-content-width: 45rem;--sl-content-pad-x: 1rem;--sl-menu-button-size: 2rem;--sl-nav-gap: var(--sl-content-pad-x);--sl-outline-offset-inside: -.1875rem;--sl-z-index-toc: 4;--sl-z-index-menu: 5;--sl-z-index-navbar: 10;--sl-z-index-skiplink: 20}:root[data-theme=light],[data-theme=light] ::backdrop{--sl-color-white: hsl(224, 10%, 10%);--sl-color-gray-1: hsl(224, 14%, 16%);--sl-color-gray-2: hsl(224, 10%, 23%);--sl-color-gray-3: hsl(224, 7%, 36%);--sl-color-gray-4: hsl(224, 6%, 56%);--sl-color-gray-5: hsl(224, 6%, 77%);--sl-color-gray-6: hsl(224, 20%, 94%);--sl-color-gray-7: hsl(224, 19%, 97%);--sl-color-black: hsl(0, 0%, 100%);--sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);--sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%);--sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%);--sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%);--sl-color-green: hsl(var(--sl-hue-green), 90%, 46%);--sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%);--sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%);--sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%);--sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%);--sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%);--sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%);--sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%);--sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%);--sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);--sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);--sl-color-accent-high: hsl(234, 80%, 30%);--sl-color-accent: hsl(234, 90%, 60%);--sl-color-accent-low: hsl(234, 88%, 90%);--sl-color-text-accent: var(--sl-color-accent);--sl-color-text-invert: var(--sl-color-black);--sl-color-bg-nav: var(--sl-color-gray-7);--sl-color-bg-sidebar: var(--sl-color-bg);--sl-color-bg-inline-code: var(--sl-color-gray-6);--sl-color-bg-accent: var(--sl-color-accent);--sl-color-hairline-light: var(--sl-color-gray-6);--sl-color-hairline-shade: var(--sl-color-gray-6);--sl-color-backdrop-overlay: hsla(225, 9%, 36%, .66);--sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, .06), 0px 2px 1px hsla(0, 0%, 0%, .06);--sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, .03), 0px 5px 2px hsla(0, 0%, 0%, .03), 0px 3px 2px hsla(0, 0%, 0%, .06), 0px 1px 1px hsla(0, 0%, 0%, .06);--sl-shadow-lg: 0px 25px 7px rgba(0, 0, 0, .01), 0px 16px 6px hsla(0, 0%, 0%, .03), 0px 9px 5px hsla(223, 13%, 10%, .08), 0px 4px 4px hsla(0, 0%, 0%, .16), 0px 4px 2px hsla(0, 0%, 0%, .04)}@media (min-width: 50em){:root{--sl-nav-height: 4rem;--sl-nav-pad-x: 1.5rem;--sl-text-h1: var(--sl-text-5xl);--sl-text-h2: var(--sl-text-4xl);--sl-text-h3: var(--sl-text-3xl);--sl-text-h4: var(--sl-text-2xl)}}@media (min-width: 72rem){:root{--sl-content-pad-x: 1.5rem;--sl-mobile-toc-height: 0rem}}*,*:before,*:after{box-sizing:border-box}*{margin:0}html{color-scheme:dark;accent-color:var(--sl-color-accent)}html[data-theme=light]{color-scheme:light}body{font-family:var(--__sl-font);line-height:var(--sl-line-height);-webkit-font-smoothing:antialiased;color:var(--sl-color-text);background-color:var(--sl-color-bg)}input,button,textarea,select{font:inherit}p,h1,h2,h3,h4,h5,h6,code{overflow-wrap:anywhere}code{font-family:var(--__sl-font-mono)}:root{--astro-code-color-text: var(--sl-color-white);--astro-code-color-background: var(--sl-color-gray-6);--astro-code-token-constant: var(--sl-color-blue-high);--astro-code-token-string: var(--sl-color-green-high);--astro-code-token-comment: var(--sl-color-gray-2);--astro-code-token-keyword: var(--sl-color-purple-high);--astro-code-token-parameter: var(--sl-color-red-high);--astro-code-token-function: var(--sl-color-red-high);--astro-code-token-string-expression: var(--sl-color-green-high);--astro-code-token-punctuation: var(--sl-color-gray-2);--astro-code-token-link: var(--sl-color-blue-high)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sl-hidden{display:none}.sl-flex{display:flex}.sl-block{display:block}@media (min-width: 50rem){.md\:sl-hidden{display:none}.md\:sl-flex{display:flex}.md\:sl-block{display:block}}@media (min-width: 72rem){.lg\:sl-hidden{display:none}.lg\:sl-flex{display:flex}.lg\:sl-block{display:block}}[data-theme=light] .light\:sl-hidden,[data-theme=dark] .dark\:sl-hidden{display:none}[dir=rtl] .rtl\:flip:not(:where([dir=rtl] [dir=ltr] *)){transform:scaleX(-1)}.sl-banner:where(.astro-l5t7fgan){--__sl-banner-text: var(--sl-color-banner-text, var(--sl-color-text-invert));padding:var(--sl-nav-pad-y) var(--sl-nav-pad-x);background-color:var(--sl-color-banner-bg, var(--sl-color-bg-accent));color:var(--__sl-banner-text);line-height:var(--sl-line-height-headings);text-align:center;text-wrap:balance;box-shadow:var(--sl-shadow-sm)}.sl-banner:where(.astro-l5t7fgan) a{color:var(--__sl-banner-text)}.content-panel:where(.astro-lguznrne){padding:1.5rem var(--sl-content-pad-x)}.content-panel:where(.astro-lguznrne)+.content-panel:where(.astro-lguznrne){border-top:1px solid var(--sl-color-hairline)}.sl-container:where(.astro-lguznrne){max-width:var(--sl-content-width)}.sl-container:where(.astro-lguznrne)>*+*{margin-top:1.5rem}@media (min-width: 72rem){.sl-container:where(.astro-lguznrne){margin-inline:var(--sl-content-margin-inline, auto)}}p:where(.astro-7yvuoepg){border:1px solid var(--sl-color-orange);padding:.75em 1em;background-color:var(--sl-color-orange-low);color:var(--sl-color-orange-high);width:-moz-max-content;width:max-content;max-width:100%;align-items:center;gap:.75em;font-size:var(--sl-text-body-sm);line-height:var(--sl-line-height-headings)}a:where(.astro-s7sgl367){gap:.5rem;align-items:center;text-decoration:none;color:var(--sl-color-gray-3)}a:where(.astro-s7sgl367):hover{color:var(--sl-color-white)}.pagination-links:where(.astro-p44bw66x){display:grid;grid-template-columns:repeat(auto-fit,minmax(min(18rem,100%),1fr));gap:1rem}a:where(.astro-p44bw66x){display:flex;align-items:center;justify-content:flex-start;gap:.5rem;width:100%;flex-basis:calc(50% - .5rem);flex-grow:1;border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding:1rem;text-decoration:none;color:var(--sl-color-gray-2);box-shadow:var(--sl-shadow-md);overflow-wrap:anywhere}:where(.astro-p44bw66x)[rel=next]{justify-content:end;text-align:end;flex-direction:row-reverse}a:where(.astro-p44bw66x):hover{border-color:var(--sl-color-gray-2)}.link-title:where(.astro-p44bw66x){color:var(--sl-color-white);font-size:var(--sl-text-2xl);line-height:var(--sl-line-height-headings)}svg:where(.astro-p44bw66x){flex-shrink:0}footer:where(.astro-caekvz2o){flex-direction:column;gap:1.5rem}.meta:where(.astro-caekvz2o){gap:.75rem 3rem;justify-content:space-between;flex-wrap:wrap;margin-top:3rem;font-size:var(--sl-text-sm);color:var(--sl-color-gray-3)}.meta:where(.astro-caekvz2o)>p:only-child{margin-inline-start:auto}.kudos:where(.astro-caekvz2o){align-items:center;gap:.5em;margin:1.5rem auto;font-size:var(--sl-text-xs);text-decoration:none;color:var(--sl-color-gray-3)}.kudos:where(.astro-caekvz2o) svg{color:var(--sl-color-orange)}.kudos:where(.astro-caekvz2o):hover{color:var(--sl-color-white)}label:where(.astro-jhmexls3){--sl-label-icon-size: .875rem;--sl-caret-size: 1.25rem;--sl-inline-padding: .5rem;position:relative;display:flex;align-items:center;gap:.25rem;color:var(--sl-color-gray-1)}label:where(.astro-jhmexls3):hover{color:var(--sl-color-gray-2)}.icon:where(.astro-jhmexls3){position:absolute;top:50%;transform:translateY(-50%);pointer-events:none}.label-icon:where(.astro-jhmexls3){font-size:var(--sl-label-icon-size);inset-inline-start:0}.caret:where(.astro-jhmexls3){font-size:var(--sl-caret-size);inset-inline-end:0}select:where(.astro-jhmexls3){border:0;padding-block:.625rem;padding-inline:calc(var(--sl-label-icon-size) + var(--sl-inline-padding) + .25rem) calc(var(--sl-caret-size) + var(--sl-inline-padding) + .25rem);margin-inline:calc(var(--sl-inline-padding) * -1);width:calc(var(--sl-select-width) + var(--sl-inline-padding) * 2);background-color:transparent;text-overflow:ellipsis;color:inherit;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}option:where(.astro-jhmexls3){background-color:var(--sl-color-bg-nav);color:var(--sl-color-gray-1)}@media (min-width: 50rem){select:where(.astro-jhmexls3){font-size:var(--sl-text-sm)}}/*! @docsearch/css Modal 3.6.0 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::-moz-placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}.DocSearch-VisuallyHiddenForAccessibility{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}:root{--docsearch-primary-color: var(--sl-color-text-accent);--docsearch-text-color: var(--sl-color-text);--docsearch-spacing: 12px;--docsearch-icon-stroke-width: 1.4;--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-muted-color: var(--sl-color-gray-3);--docsearch-container-background: var(--sl-color-backdrop-overlay);--docsearch-modal-width: 560px;--docsearch-modal-height: 600px;--docsearch-modal-background: var(--sl-color-gray-6);--docsearch-modal-shadow: var(--sl-shadow-lg);--docsearch-searchbox-height: 56px;--docsearch-searchbox-background: var(--sl-color-gray-7, var(--sl-color-gray-6));--docsearch-searchbox-focus-background: var(--sl-color-black);--docsearch-searchbox-shadow: inset 0 0 0 1px var(--docsearch-primary-color);--docsearch-hit-height: 56px;--docsearch-hit-color: var(--sl-color-white);--docsearch-hit-active-color: var(--sl-color-black);--docsearch-hit-background: var(--sl-color-black);--docsearch-key-gradient: linear-gradient( var(--sl-color-bg-inline-code) 0%, var(--sl-color-bg-inline-code) 100% );--docsearch-footer-height: 44px;--docsearch-footer-background: var(--sl-color-black);--docsearch-footer-shadow: 0 -1px 0 0 var(--sl-color-hairline-light)}.DocSearch-Modal{border:1px solid var(--sl-color-hairline-light)}.DocSearch-Logo svg *{fill:var(--docsearch-muted-color)}.DocSearch-Button{display:flex;align-items:center;gap:.5rem;border:0;background-color:transparent;color:var(--sl-color-gray-1);cursor:pointer;height:2.5rem;font-size:var(--sl-text-xl)}.DocSearch-Button-Container{display:contents}.DocSearch-Search-Icon{width:.875em;height:.875em;stroke-width:.125rem}.DocSearch-Button-Placeholder,.DocSearch-Button-Keys,.DocSearch-Button-Key{display:none}@media (min-width: 50rem){sl-doc-search{width:100%}.DocSearch-Button{border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding-inline-start:.75rem;padding-inline-end:1rem;background-color:var(--sl-color-black);color:var(--sl-color-gray-2);font-size:var(--sl-text-sm);width:100%;max-width:22rem}.DocSearch-Button:hover{border-color:var(--sl-color-gray-2);color:var(--sl-color-white)}.DocSearch-Button-Placeholder,.DocSearch-Button-Keys{display:flex}.DocSearch-Button-Keys{margin-inline-start:auto}.DocSearch-Button-Keys:before{content:"";width:1em;height:1em;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 2H7a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5Zm3 15a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10Z'%3E%3C/path%3E%3Cpath d='M15.293 6.707a1 1 0 1 1 1.414 1.414l-8.485 8.486a1 1 0 0 1-1.414-1.415l8.485-8.485Z'%3E%3C/path%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 2H7a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5Zm3 15a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10Z'%3E%3C/path%3E%3Cpath d='M15.293 6.707a1 1 0 1 1 1.414 1.414l-8.485 8.486a1 1 0 0 1-1.414-1.415l8.485-8.485Z'%3E%3C/path%3E%3C/svg%3E");-webkit-mask-size:100%;mask-size:100%;background-color:currentColor}}.site-title:where(.astro-rz6ifacj){align-items:center;gap:var(--sl-nav-gap);font-size:var(--sl-text-h4);font-weight:600;color:var(--sl-color-text-accent);text-decoration:none;white-space:nowrap}img:where(.astro-rz6ifacj){height:calc(var(--sl-nav-height) - 2 * var(--sl-nav-pad-y));width:auto;max-width:100%;-o-object-fit:contain;object-fit:contain;-o-object-position:0 50%;object-position:0 50%}a:where(.astro-n2zxb76s){color:var(--sl-color-text-accent);padding:.5em;margin:-.5em}a:where(.astro-n2zxb76s):hover{opacity:.66}.nav-item:where(.astro-ymhdp2rl){font-size:var(--sl-text-base);font-weight:600;color:var(--sl-color-gray-2);border:1px solid var(--sl-color-text-accent);text-decoration:none;display:flex;align-items:center;border-radius:.25rem;padding:0 1rem}.nav-item:where(.astro-ymhdp2rl):hover{background-color:var(--sl-color-text-accent);color:var(--sl-color-text-invert)}.nav-item:where(.astro-ymhdp2rl).current{background-color:var(--sl-color-white);color:var(--sl-color-text-invert)}.header:where(.astro-bhd2tec3){gap:var(--sl-nav-gap);justify-content:space-between;align-items:center;height:100%}.title-wrapper:where(.astro-bhd2tec3){overflow:hidden}.right-group:where(.astro-bhd2tec3),.social-icons:where(.astro-bhd2tec3){gap:1rem;align-items:center}.social-icons:where(.astro-bhd2tec3):after{content:"";height:2rem;border-inline-end:1px solid var(--sl-color-gray-5)}@media (min-width: 50rem){:root[data-has-sidebar]{--__sidebar-pad: calc(2 * var(--sl-nav-pad-x))}:root:not([data-has-toc]){--__toc-width: 0rem}.header:where(.astro-bhd2tec3){--__sidebar-width: max(0rem, var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x));--__main-column-fr: calc( ( 100% + var(--__sidebar-pad, 0rem) - var(--__toc-width, var(--sl-sidebar-width)) - (2 * var(--__toc-width, var(--sl-nav-pad-x))) - var(--sl-content-inline-start, 0rem) - var(--sl-content-width) ) / 2 );display:grid;grid-template-columns:minmax(calc(var(--__sidebar-width) + max(0rem,var(--__main-column-fr) - var(--sl-nav-gap))),auto) 1fr auto;align-content:center}}.hero:where(.astro-xjwbnyvw){display:grid;align-items:center;gap:1rem;padding-bottom:1rem}.hero:where(.astro-xjwbnyvw)>img:where(.astro-xjwbnyvw),.hero:where(.astro-xjwbnyvw)>.hero-html:where(.astro-xjwbnyvw){-o-object-fit:contain;object-fit:contain;width:min(70%,20rem);height:auto;margin-inline:auto}.stack:where(.astro-xjwbnyvw){flex-direction:column;gap:clamp(1.5rem,calc(1.5rem + 1vw),2rem);text-align:center}.copy:where(.astro-xjwbnyvw){flex-direction:column;gap:1rem;align-items:center}.copy:where(.astro-xjwbnyvw)>:where(.astro-xjwbnyvw){max-width:50ch}h1:where(.astro-xjwbnyvw){font-size:clamp(var(--sl-text-3xl),calc(.25rem + 5vw),var(--sl-text-6xl));line-height:var(--sl-line-height-headings);font-weight:600;color:var(--sl-color-white)}.tagline:where(.astro-xjwbnyvw){font-size:clamp(var(--sl-text-base),calc(.0625rem + 2vw),var(--sl-text-xl));color:var(--sl-color-gray-2)}.actions:where(.astro-xjwbnyvw){gap:1rem 2rem;flex-wrap:wrap;justify-content:center}@media (min-width: 50rem){.hero:where(.astro-xjwbnyvw){grid-template-columns:7fr 4fr;gap:3%;padding-block:clamp(2.5rem,calc(1rem + 10vmin),10rem)}.hero:where(.astro-xjwbnyvw)>img:where(.astro-xjwbnyvw),.hero:where(.astro-xjwbnyvw)>.hero-html:where(.astro-xjwbnyvw){order:2;width:min(100%,25rem)}.stack:where(.astro-xjwbnyvw){text-align:start}.copy:where(.astro-xjwbnyvw){align-items:flex-start}.actions:where(.astro-xjwbnyvw){justify-content:flex-start}}.sl-markdown-content :not(a,strong,em,del,span,input,code)+:not(a,strong,em,del,span,input,code,:where(.not-content *)){margin-top:1rem}.sl-markdown-content :not(h1,h2,h3,h4,h5,h6)+:is(h1,h2,h3,h4,h5,h6):not(:where(.not-content *)){margin-top:1.5em}.sl-markdown-content li+li:not(:where(.not-content *)),.sl-markdown-content dt+dt:not(:where(.not-content *)),.sl-markdown-content dt+dd:not(:where(.not-content *)),.sl-markdown-content dd+dd:not(:where(.not-content *)){margin-top:.25rem}.sl-markdown-content li:not(:where(.not-content *)){overflow-wrap:anywhere}.sl-markdown-content li>:last-child:not(li,ul,ol):not(a,strong,em,del,span,input,:where(.not-content *)){margin-bottom:1.25rem}.sl-markdown-content dt:not(:where(.not-content *)){font-weight:700}.sl-markdown-content dd:not(:where(.not-content *)){padding-inline-start:1rem}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6):not(:where(.not-content *)){color:var(--sl-color-white);line-height:var(--sl-line-height-headings);font-weight:600}.sl-markdown-content :is(img,picture,video,canvas,svg,iframe):not(:where(.not-content *)){display:block;max-width:100%;height:auto}.sl-markdown-content h1:not(:where(.not-content *)){font-size:var(--sl-text-h1)}.sl-markdown-content h2:not(:where(.not-content *)){font-size:var(--sl-text-h2)}.sl-markdown-content h3:not(:where(.not-content *)){font-size:var(--sl-text-h3)}.sl-markdown-content h4:not(:where(.not-content *)){font-size:var(--sl-text-h4)}.sl-markdown-content h5:not(:where(.not-content *)){font-size:var(--sl-text-h5)}.sl-markdown-content h6:not(:where(.not-content *)){font-size:var(--sl-text-h6)}.sl-markdown-content a:not(:where(.not-content *)){color:var(--sl-color-text-accent)}.sl-markdown-content a:hover:not(:where(.not-content *)){color:var(--sl-color-white)}.sl-markdown-content code:not(:where(.not-content *)){background-color:var(--sl-color-bg-inline-code);margin-block:-.125rem;padding:.125rem .375rem;font-size:var(--sl-text-code-sm)}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6) code{font-size:inherit}.sl-markdown-content pre:not(:where(.not-content *)){border:1px solid var(--sl-color-gray-5);padding:.75rem 1rem;font-size:var(--sl-text-code);-moz-tab-size:2;-o-tab-size:2;tab-size:2}.sl-markdown-content pre code:not(:where(.not-content *)){all:unset;font-family:var(--__sl-font-mono)}.sl-markdown-content blockquote:not(:where(.not-content *)){border-inline-start:1px solid var(--sl-color-gray-5);padding-inline-start:1rem}.sl-markdown-content table:not(:where(.not-content *)){display:block;overflow:auto;border-spacing:0}.sl-markdown-content :is(th,td):not(:where(.not-content *)){border-bottom:1px solid var(--sl-color-gray-5);padding:.5rem 1rem;vertical-align:baseline}.sl-markdown-content :is(th:first-child,td:first-child):not(:where(.not-content *)){padding-inline-start:0}.sl-markdown-content :is(th:last-child,td:last-child):not(:where(.not-content *)){padding-inline-end:0}.sl-markdown-content th:not(:where(.not-content *)){color:var(--sl-color-white);font-weight:600}.sl-markdown-content th:not([align]):not(:where(.not-content *)){text-align:start}.sl-markdown-content .starlight-aside :is(th,td,hr,blockquote):not(:where(.not-content *)){border-color:var(--sl-color-gray-4)}@supports (border-color: color-mix(in srgb,var(--sl-color-asides-text-accent) 30%,transparent)){.sl-markdown-content .starlight-aside :is(th,td,hr,blockquote):not(:where(.not-content *)){border-color:color-mix(in srgb,var(--sl-color-asides-text-accent) 30%,transparent)}}@supports (border-color: color-mix(in srgb,var(--sl-color-asides-text-accent) 12%,transparent)){.sl-markdown-content .starlight-aside code:not(:where(.not-content *)){background-color:color-mix(in srgb,var(--sl-color-asides-text-accent) 12%,transparent)}}.sl-markdown-content hr:not(:where(.not-content *)){border:0;border-bottom:1px solid var(--sl-color-hairline)}.sl-markdown-content details:not(:where(.not-content *)){--sl-details-border-color: var(--sl-color-gray-5);--sl-details-border-color--hover: var(--sl-color-text-accent);border-inline-start:2px solid var(--sl-details-border-color);padding-inline-start:1rem}.sl-markdown-content details:not([open]):hover:not(:where(.not-content *)),.sl-markdown-content details:has(>summary:hover):not(:where(.not-content *)){border-color:var(--sl-details-border-color--hover)}.sl-markdown-content summary:not(:where(.not-content *)){color:var(--sl-color-white);cursor:pointer;display:block;font-weight:600;margin-inline-start:-.5rem;padding-inline-start:.5rem}.sl-markdown-content details[open]>summary:not(:where(.not-content *)){margin-bottom:1rem}.sl-markdown-content summary:not(:where(.not-content *))::marker,.sl-markdown-content summary:not(:where(.not-content *))::-webkit-details-marker{display:none}.sl-markdown-content summary:not(:where(.not-content *)):before{--sl-details-marker-size: 1.25rem;background-color:currentColor;content:"";display:inline-block;height:var(--sl-details-marker-size);width:var(--sl-details-marker-size);margin-inline:calc((var(--sl-details-marker-size) / 4) * -1) .25rem;vertical-align:middle;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}@media (prefers-reduced-motion: no-preference){.sl-markdown-content summary:not(:where(.not-content *)):before{transition:transform .2s ease-in-out}}.sl-markdown-content details[open]>summary:not(:where(.not-content *)):before{transform:rotate(90deg)}[dir=rtl] .sl-markdown-content summary:not(:where(.not-content *)):before,.sl-markdown-content [dir=rtl] summary:not(:where(.not-content *)):before{transform:rotate(180deg)}.sl-markdown-content summary:not(:where(.not-content *)) p:only-child{display:inline}.sl-markdown-content .starlight-aside details:not(:where(.not-content *)){--sl-details-border-color: var(--sl-color-asides-border);--sl-details-border-color--hover: var(--sl-color-asides-text-accent)}[data-mobile-menu-expanded]{overflow:hidden}@media (min-width: 50rem){[data-mobile-menu-expanded]{overflow:auto}}button:where(.astro-5izeyrng){position:fixed;top:calc((var(--sl-nav-height) - var(--sl-menu-button-size)) / 2);inset-inline-end:var(--sl-nav-pad-x);z-index:var(--sl-z-index-navbar);border:0;border-radius:50%;width:var(--sl-menu-button-size);height:var(--sl-menu-button-size);padding:.5rem;background-color:var(--sl-color-white);color:var(--sl-color-black);box-shadow:var(--sl-shadow-md);cursor:pointer}:where(.astro-5izeyrng)[aria-expanded=true] button:where(.astro-5izeyrng){background-color:var(--sl-color-gray-2);box-shadow:none}[data-theme=light] button:where(.astro-5izeyrng){background-color:var(--sl-color-black);color:var(--sl-color-white)}[data-theme=light] :where(.astro-5izeyrng)[aria-expanded=true] button:where(.astro-5izeyrng){background-color:var(--sl-color-gray-5)}.page:where(.astro-euql5f24){flex-direction:column;min-height:100vh}.header:where(.astro-euql5f24){z-index:var(--sl-z-index-navbar);position:fixed;inset-inline-start:0;inset-block-start:0;width:100%;height:var(--sl-nav-height);border-bottom:1px solid var(--sl-color-hairline-shade);padding:var(--sl-nav-pad-y) var(--sl-nav-pad-x);padding-inline-end:var(--sl-nav-pad-x);background-color:var(--sl-color-bg-nav)}[data-has-sidebar] .header:where(.astro-euql5f24){padding-inline-end:calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size))}.sidebar-pane:where(.astro-euql5f24){visibility:var(--sl-sidebar-visibility, hidden);position:fixed;z-index:var(--sl-z-index-menu);inset-block:var(--sl-nav-height) 0;inset-inline-start:0;width:100%;background-color:var(--sl-color-black);overflow-y:auto}[aria-expanded=true]~.sidebar-pane:where(.astro-euql5f24){--sl-sidebar-visibility: visible}.sidebar-content:where(.astro-euql5f24){height:100%;min-height:-moz-max-content;min-height:max-content;padding:1rem var(--sl-sidebar-pad-x) 0;flex-direction:column;gap:1rem}@media (min-width: 50rem){.sidebar-content:where(.astro-euql5f24):after{content:"";padding-bottom:1px}}.main-frame:where(.astro-euql5f24){padding-top:calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));padding-inline-start:var(--sl-content-inline-start)}@media (min-width: 50rem){[data-has-sidebar] .header:where(.astro-euql5f24){padding-inline-end:var(--sl-nav-pad-x)}.sidebar-pane:where(.astro-euql5f24){--sl-sidebar-visibility: visible;width:var(--sl-sidebar-width);background-color:var(--sl-color-bg-sidebar);border-inline-end:1px solid var(--sl-color-hairline-shade)}}ul:where(.astro-wzyvean3){padding:0;list-style:none}a:where(.astro-wzyvean3){--pad-inline: .5rem;display:block;border-radius:.25rem;padding-block:.25rem;padding-inline:calc(1rem * var(--depth) + var(--pad-inline)) var(--pad-inline);line-height:1.25}a:where(.astro-wzyvean3)[aria-current=true]{color:var(--sl-color-text-accent)}.isMobile:where(.astro-wzyvean3) a:where(.astro-wzyvean3){--pad-inline: 1rem;display:flex;justify-content:space-between;gap:var(--pad-inline);border-top:1px solid var(--sl-color-gray-6);border-radius:0;padding-block:.5rem;color:var(--sl-color-text);font-size:var(--sl-text-sm);text-decoration:none;outline-offset:var(--sl-outline-offset-inside)}.isMobile:where(.astro-wzyvean3):first-child>li:where(.astro-wzyvean3):first-child>a:where(.astro-wzyvean3){border-top:0}.isMobile:where(.astro-wzyvean3) a:where(.astro-wzyvean3)[aria-current=true],.isMobile:where(.astro-wzyvean3) a:where(.astro-wzyvean3)[aria-current=true]:hover,.isMobile:where(.astro-wzyvean3) a:where(.astro-wzyvean3)[aria-current=true]:focus{color:var(--sl-color-white);background-color:unset}.isMobile:where(.astro-wzyvean3) a:where(.astro-wzyvean3)[aria-current=true]:after{content:"";width:1rem;background-color:var(--sl-color-text-accent);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;flex-shrink:0}nav:where(.astro-n3ayd5ey){position:fixed;z-index:var(--sl-z-index-toc);top:calc(var(--sl-nav-height) - 1px);inset-inline:0;border-top:1px solid var(--sl-color-gray-5);background-color:var(--sl-color-bg-nav)}@media (min-width: 50rem){nav:where(.astro-n3ayd5ey){inset-inline-start:var(--sl-content-inline-start, 0)}}summary:where(.astro-n3ayd5ey){gap:.5rem;align-items:center;height:var(--sl-mobile-toc-height);border-bottom:1px solid var(--sl-color-hairline-shade);padding:.5rem 1rem;font-size:var(--sl-text-xs);outline-offset:var(--sl-outline-offset-inside)}summary:where(.astro-n3ayd5ey)::marker,summary:where(.astro-n3ayd5ey)::-webkit-details-marker{display:none}.toggle:where(.astro-n3ayd5ey){flex-shrink:0;gap:1rem;align-items:center;justify-content:space-between;border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding-block:.5rem;padding-inline-start:.75rem;padding-inline-end:.5rem;line-height:1;background-color:var(--sl-color-black);-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer}details:where(.astro-n3ayd5ey)[open] .toggle:where(.astro-n3ayd5ey){color:var(--sl-color-white);border-color:var(--sl-color-accent)}details:where(.astro-n3ayd5ey) .toggle:where(.astro-n3ayd5ey):hover{color:var(--sl-color-white);border-color:var(--sl-color-gray-2)}[dir=rtl] .caret:where(.astro-n3ayd5ey){transform:rotate(180deg)}details:where(.astro-n3ayd5ey)[open] .caret:where(.astro-n3ayd5ey){transform:rotate(90deg)}.display-current:where(.astro-n3ayd5ey){white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:var(--sl-color-white)}.dropdown:where(.astro-n3ayd5ey){--border-top: 1px;margin-top:calc(-1 * var(--border-top));border:var(--border-top) solid var(--sl-color-gray-6);border-top-color:var(--sl-color-hairline-shade);max-height:calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));overflow-y:auto;background-color:var(--sl-color-black);box-shadow:var(--sl-shadow-md);overscroll-behavior:contain}.right-sidebar-panel:where(.astro-ghvgk3qj){padding:1rem var(--sl-sidebar-pad-x)}.sl-container:where(.astro-ghvgk3qj){width:calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x))}.right-sidebar-panel:where(.astro-ghvgk3qj) h2{color:var(--sl-color-white);font-size:var(--sl-text-h5);font-weight:600;line-height:var(--sl-line-height-headings);margin-bottom:.5rem}.right-sidebar-panel:where(.astro-ghvgk3qj) :where(a){display:block;font-size:var(--sl-text-xs);text-decoration:none;color:var(--sl-color-gray-3);overflow-wrap:anywhere}.right-sidebar-panel:where(.astro-ghvgk3qj) :where(a):hover{color:var(--sl-color-white)}@media (min-width: 72rem){.sl-container:where(.astro-ghvgk3qj){max-width:calc(((100vw - var(--sl-sidebar-width) - 2 * var(--sl-content-pad-x) - 2 * var(--sl-sidebar-pad-x)) * .25))}}h1:where(.astro-j5v6lccq){margin-top:1rem;font-size:var(--sl-text-h1);line-height:var(--sl-line-height-headings);font-weight:600;color:var(--sl-color-white)}.social-icons:where(.astro-4m5yeizb){margin-inline-end:auto;gap:1rem;align-items:center;padding-block:1rem}.social-icons:where(.astro-4m5yeizb):empty{display:none}.mobile-preferences:where(.astro-4m5yeizb){justify-content:space-between;flex-wrap:wrap;border-top:1px solid var(--sl-color-gray-6);-moz-column-gap:1rem;column-gap:1rem;padding:.5rem 0}sl-sidebar-state-persist:where(.astro-evkecqcj){display:contents}ul:where(.astro-rzv34nq5){--sl-sidebar-item-padding-inline: .5rem;list-style:none;padding:0}li:where(.astro-rzv34nq5){overflow-wrap:anywhere}ul:where(.astro-rzv34nq5) ul:where(.astro-rzv34nq5) li:where(.astro-rzv34nq5){margin-inline-start:var(--sl-sidebar-item-padding-inline);border-inline-start:1px solid var(--sl-color-hairline-light);padding-inline-start:var(--sl-sidebar-item-padding-inline)}.large:where(.astro-rzv34nq5){font-size:var(--sl-text-lg);font-weight:600;color:var(--sl-color-white)}.top-level:where(.astro-rzv34nq5)>li:where(.astro-rzv34nq5)+li:where(.astro-rzv34nq5){margin-top:.75rem}summary:where(.astro-rzv34nq5){display:flex;align-items:center;justify-content:space-between;padding:.2em var(--sl-sidebar-item-padding-inline);line-height:1.4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}summary:where(.astro-rzv34nq5)::marker,summary:where(.astro-rzv34nq5)::-webkit-details-marker{display:none}.caret:where(.astro-rzv34nq5){transition:transform .2s ease-in-out;flex-shrink:0}[dir=rtl] .caret:where(.astro-rzv34nq5){transform:rotate(180deg)}:where(.astro-rzv34nq5)[open]>summary:where(.astro-rzv34nq5) .caret:where(.astro-rzv34nq5){transform:rotate(90deg)}a:where(.astro-rzv34nq5){display:block;border-radius:.25rem;text-decoration:none;color:var(--sl-color-gray-2);padding:.3em var(--sl-sidebar-item-padding-inline);line-height:1.4}a:where(.astro-rzv34nq5):hover,a:where(.astro-rzv34nq5):focus{color:var(--sl-color-white)}:where(.astro-rzv34nq5)[aria-current=page],:where(.astro-rzv34nq5)[aria-current=page]:hover,:where(.astro-rzv34nq5)[aria-current=page]:focus{font-weight:600;color:var(--sl-color-text-invert);background-color:var(--sl-color-text-accent)}a:where(.astro-rzv34nq5)>:where(.astro-rzv34nq5):not(:last-child),.group-label:where(.astro-rzv34nq5)>:where(.astro-rzv34nq5):not(:last-child){margin-inline-end:.25em}@media (min-width: 50rem){.top-level:where(.astro-rzv34nq5)>li:where(.astro-rzv34nq5)+li:where(.astro-rzv34nq5){margin-top:.5rem}.large:where(.astro-rzv34nq5){font-size:var(--sl-text-base)}a:where(.astro-rzv34nq5){font-size:var(--sl-text-sm)}}a:where(.astro-3rr6ide6){clip:rect(0,0,0,0);position:fixed;top:.75rem;inset-inline-start:.75rem}a:where(.astro-3rr6ide6):focus{clip:unset;z-index:var(--sl-z-index-skiplink);display:block;padding:.5rem 1rem;text-decoration:none;color:var(--sl-color-text-invert);background-color:var(--sl-color-text-accent);box-shadow:var(--sl-shadow-lg)}.main-pane:where(.astro-zb4bp72j){isolation:isolate}@media (min-width: 72rem){.right-sidebar-container:where(.astro-zb4bp72j){order:2;position:relative;width:calc(var(--sl-sidebar-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2)}.right-sidebar:where(.astro-zb4bp72j){position:fixed;top:0;border-inline-start:1px solid var(--sl-color-gray-6);padding-top:var(--sl-nav-height);width:100%;height:100vh;overflow-y:auto;scrollbar-width:none}.main-pane:where(.astro-zb4bp72j){width:100%}[data-has-sidebar][data-has-toc] .main-pane:where(.astro-zb4bp72j){--sl-content-margin-inline: auto 0;order:1;width:calc(var(--sl-content-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2)}}.starlight-aside{padding:1rem;border-inline-start:.25rem solid var(--sl-color-asides-border);color:var(--sl-color-white)}.starlight-aside--note{--sl-color-asides-text-accent: var(--sl-color-blue-high);--sl-color-asides-border: var(--sl-color-blue);background-color:var(--sl-color-blue-low)}.starlight-aside--tip{--sl-color-asides-text-accent: var(--sl-color-purple-high);--sl-color-asides-border: var(--sl-color-purple);background-color:var(--sl-color-purple-low)}.starlight-aside--caution{--sl-color-asides-text-accent: var(--sl-color-orange-high);--sl-color-asides-border: var(--sl-color-orange);background-color:var(--sl-color-orange-low)}.starlight-aside--danger{--sl-color-asides-text-accent: var(--sl-color-red-high);--sl-color-asides-border: var(--sl-color-red);background-color:var(--sl-color-red-low)}.starlight-aside__title{display:flex;gap:.5rem;align-items:center;font-size:var(--sl-text-h5);font-weight:600;line-height:var(--sl-line-height-headings);color:var(--sl-color-asides-text-accent)}.starlight-aside__icon{font-size:1.333em;width:1em;height:1em}.starlight-aside__title+.starlight-aside__content{margin-top:.5rem}.starlight-aside__content a{color:var(--sl-color-asides-text-accent)}html[data-theme=dark] img[src*=lightModeOnly],html[data-theme=light] img[src*=darkModeOnly]{display:none!important}:root{--sl-content-width: 80rem}*,:before,:after{border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: }html,:host{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}:root{--sl-font: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--sl-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--sl-color-white: #fff;--sl-color-gray-1: #e5e7eb;--sl-color-gray-2: #d1d5db;--sl-color-gray-3: #9ca3af;--sl-color-gray-4: #4b5563;--sl-color-gray-5: #374151;--sl-color-gray-6: #1f2937;--sl-color-black: #111827;--sl-color-accent-low: #1e1b4b;--sl-color-accent: #4f46e5;--sl-color-accent-high: #c7d2fe}:root[data-theme=light]{--sl-color-white: #111827;--sl-color-gray-1: #1f2937;--sl-color-gray-2: #374151;--sl-color-gray-3: #6b7280;--sl-color-gray-4: #9ca3af;--sl-color-gray-5: #d1d5db;--sl-color-gray-6: #e5e7eb;--sl-color-gray-7: #f3f4f6;--sl-color-black: #fff;--sl-color-accent-low: #c7d2fe;--sl-color-accent: #4f46e5;--sl-color-accent-high: #312e81}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.static{position:static}.relative{position:relative}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mt-4{margin-top:1rem}.inline{display:inline}.flex{display:flex}.grid{display:grid}.w-24{width:6rem}.w-full{width:100%}.min-w-24{min-width:6rem}.flex-1{flex:1 1 0%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.py-4{padding-top:1rem;padding-bottom:1rem}.text-center{text-align:center}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media (min-width: 1024px){.lg\:flex-row{flex-direction:row}}.\[\&_\*\]\:\!mt-0 *{margin-top:0!important}html:not([data-has-toc]){--sl-mobile-toc-height: 0rem}html:not([data-has-sidebar]){--sl-content-width: 67.5rem}html{scroll-padding-top:calc(1.5rem + var(--sl-nav-height) + var(--sl-mobile-toc-height))}main:where(.astro-na4zzfem){padding-bottom:3vh}@media (min-width: 50em){:where(.astro-na4zzfem)[data-has-sidebar]{--sl-content-inline-start: var(--sl-sidebar-width)}}@media (min-width: 72em){html{scroll-padding-top:calc(1.5rem + var(--sl-nav-height))}} +:root,::backdrop{--sl-color-white: hsl(0, 0%, 100%);--sl-color-gray-1: hsl(224, 20%, 94%);--sl-color-gray-2: hsl(224, 6%, 77%);--sl-color-gray-3: hsl(224, 6%, 56%);--sl-color-gray-4: hsl(224, 7%, 36%);--sl-color-gray-5: hsl(224, 10%, 23%);--sl-color-gray-6: hsl(224, 14%, 16%);--sl-color-black: hsl(224, 10%, 10%);--sl-hue-orange: 41;--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);--sl-hue-green: 101;--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);--sl-hue-blue: 234;--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);--sl-hue-purple: 281;--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);--sl-hue-red: 339;--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);--sl-color-accent-low: hsl(224, 54%, 20%);--sl-color-accent: hsl(224, 100%, 60%);--sl-color-accent-high: hsl(224, 100%, 85%);--sl-color-text: var(--sl-color-gray-2);--sl-color-text-accent: var(--sl-color-accent-high);--sl-color-text-invert: var(--sl-color-accent-low);--sl-color-bg: var(--sl-color-black);--sl-color-bg-nav: var(--sl-color-gray-6);--sl-color-bg-sidebar: var(--sl-color-gray-6);--sl-color-bg-inline-code: var(--sl-color-gray-5);--sl-color-bg-accent: var(--sl-color-accent-high);--sl-color-hairline-light: var(--sl-color-gray-5);--sl-color-hairline: var(--sl-color-gray-6);--sl-color-hairline-shade: var(--sl-color-black);--sl-color-backdrop-overlay: hsla(223, 13%, 10%, .66);--sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, .12), 0px 2px 1px hsla(0, 0%, 0%, .24);--sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, .08), 0px 5px 2px hsla(0, 0%, 0%, .08), 0px 3px 2px hsla(0, 0%, 0%, .12), 0px 1px 1px hsla(0, 0%, 0%, .15);--sl-shadow-lg: 0px 25px 7px hsla(0, 0%, 0%, .03), 0px 16px 6px hsla(0, 0%, 0%, .1), 0px 9px 5px hsla(223, 13%, 10%, .33), 0px 4px 4px hsla(0, 0%, 0%, .75), 0px 4px 2px hsla(0, 0%, 0%, .25);--sl-text-2xs: .75rem;--sl-text-xs: .8125rem;--sl-text-sm: .875rem;--sl-text-base: 1rem;--sl-text-lg: 1.125rem;--sl-text-xl: 1.25rem;--sl-text-2xl: 1.5rem;--sl-text-3xl: 1.8125rem;--sl-text-4xl: 2.1875rem;--sl-text-5xl: 2.625rem;--sl-text-6xl: 4rem;--sl-text-body: var(--sl-text-base);--sl-text-body-sm: var(--sl-text-xs);--sl-text-code: var(--sl-text-sm);--sl-text-code-sm: var(--sl-text-xs);--sl-text-h1: var(--sl-text-4xl);--sl-text-h2: var(--sl-text-3xl);--sl-text-h3: var(--sl-text-2xl);--sl-text-h4: var(--sl-text-xl);--sl-text-h5: var(--sl-text-lg);--sl-line-height: 1.75;--sl-line-height-headings: 1.2;--sl-font-system: 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";--sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);--__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), var(--sl-font-system-mono);--sl-nav-height: 3.5rem;--sl-nav-pad-x: 1rem;--sl-nav-pad-y: .75rem;--sl-mobile-toc-height: 3rem;--sl-sidebar-width: 18.75rem;--sl-sidebar-pad-x: 1rem;--sl-content-width: 45rem;--sl-content-pad-x: 1rem;--sl-menu-button-size: 2rem;--sl-nav-gap: var(--sl-content-pad-x);--sl-outline-offset-inside: -.1875rem;--sl-z-index-toc: 4;--sl-z-index-menu: 5;--sl-z-index-navbar: 10;--sl-z-index-skiplink: 20}:root[data-theme=light],[data-theme=light] ::backdrop{--sl-color-white: hsl(224, 10%, 10%);--sl-color-gray-1: hsl(224, 14%, 16%);--sl-color-gray-2: hsl(224, 10%, 23%);--sl-color-gray-3: hsl(224, 7%, 36%);--sl-color-gray-4: hsl(224, 6%, 56%);--sl-color-gray-5: hsl(224, 6%, 77%);--sl-color-gray-6: hsl(224, 20%, 94%);--sl-color-gray-7: hsl(224, 19%, 97%);--sl-color-black: hsl(0, 0%, 100%);--sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);--sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%);--sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%);--sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%);--sl-color-green: hsl(var(--sl-hue-green), 90%, 46%);--sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%);--sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%);--sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%);--sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%);--sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%);--sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%);--sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%);--sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%);--sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);--sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);--sl-color-accent-high: hsl(234, 80%, 30%);--sl-color-accent: hsl(234, 90%, 60%);--sl-color-accent-low: hsl(234, 88%, 90%);--sl-color-text-accent: var(--sl-color-accent);--sl-color-text-invert: var(--sl-color-black);--sl-color-bg-nav: var(--sl-color-gray-7);--sl-color-bg-sidebar: var(--sl-color-bg);--sl-color-bg-inline-code: var(--sl-color-gray-6);--sl-color-bg-accent: var(--sl-color-accent);--sl-color-hairline-light: var(--sl-color-gray-6);--sl-color-hairline-shade: var(--sl-color-gray-6);--sl-color-backdrop-overlay: hsla(225, 9%, 36%, .66);--sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, .06), 0px 2px 1px hsla(0, 0%, 0%, .06);--sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, .03), 0px 5px 2px hsla(0, 0%, 0%, .03), 0px 3px 2px hsla(0, 0%, 0%, .06), 0px 1px 1px hsla(0, 0%, 0%, .06);--sl-shadow-lg: 0px 25px 7px rgba(0, 0, 0, .01), 0px 16px 6px hsla(0, 0%, 0%, .03), 0px 9px 5px hsla(223, 13%, 10%, .08), 0px 4px 4px hsla(0, 0%, 0%, .16), 0px 4px 2px hsla(0, 0%, 0%, .04)}@media (min-width: 50em){:root{--sl-nav-height: 4rem;--sl-nav-pad-x: 1.5rem;--sl-text-h1: var(--sl-text-5xl);--sl-text-h2: var(--sl-text-4xl);--sl-text-h3: var(--sl-text-3xl);--sl-text-h4: var(--sl-text-2xl)}}@media (min-width: 72rem){:root{--sl-content-pad-x: 1.5rem;--sl-mobile-toc-height: 0rem}}*,*:before,*:after{box-sizing:border-box}*{margin:0}html{color-scheme:dark;accent-color:var(--sl-color-accent)}html[data-theme=light]{color-scheme:light}body{font-family:var(--__sl-font);line-height:var(--sl-line-height);-webkit-font-smoothing:antialiased;color:var(--sl-color-text);background-color:var(--sl-color-bg)}input,button,textarea,select{font:inherit}p,h1,h2,h3,h4,h5,h6,code{overflow-wrap:anywhere}code{font-family:var(--__sl-font-mono)}:root{--astro-code-color-text: var(--sl-color-white);--astro-code-color-background: var(--sl-color-gray-6);--astro-code-token-constant: var(--sl-color-blue-high);--astro-code-token-string: var(--sl-color-green-high);--astro-code-token-comment: var(--sl-color-gray-2);--astro-code-token-keyword: var(--sl-color-purple-high);--astro-code-token-parameter: var(--sl-color-red-high);--astro-code-token-function: var(--sl-color-red-high);--astro-code-token-string-expression: var(--sl-color-green-high);--astro-code-token-punctuation: var(--sl-color-gray-2);--astro-code-token-link: var(--sl-color-blue-high)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sl-hidden{display:none}.sl-flex{display:flex}.sl-block{display:block}@media (min-width: 50rem){.md\:sl-hidden{display:none}.md\:sl-flex{display:flex}.md\:sl-block{display:block}}@media (min-width: 72rem){.lg\:sl-hidden{display:none}.lg\:sl-flex{display:flex}.lg\:sl-block{display:block}}[data-theme=light] .light\:sl-hidden,[data-theme=dark] .dark\:sl-hidden{display:none}[dir=rtl] .rtl\:flip:not(:where([dir=rtl] [dir=ltr] *)){transform:scaleX(-1)}.sl-banner:where(.astro-oqd6xdn6){--__sl-banner-text: var(--sl-color-banner-text, var(--sl-color-text-invert));padding:var(--sl-nav-pad-y) var(--sl-nav-pad-x);background-color:var(--sl-color-banner-bg, var(--sl-color-bg-accent));color:var(--__sl-banner-text);line-height:var(--sl-line-height-headings);text-align:center;text-wrap:balance;box-shadow:var(--sl-shadow-sm)}.sl-banner:where(.astro-oqd6xdn6) a{color:var(--__sl-banner-text)}.content-panel:where(.astro-6zdnjy42){padding:1.5rem var(--sl-content-pad-x)}.content-panel:where(.astro-6zdnjy42)+.content-panel:where(.astro-6zdnjy42){border-top:1px solid var(--sl-color-hairline)}.sl-container:where(.astro-6zdnjy42){max-width:var(--sl-content-width)}.sl-container:where(.astro-6zdnjy42)>*+*{margin-top:1.5rem}@media (min-width: 72rem){.sl-container:where(.astro-6zdnjy42){margin-inline:var(--sl-content-margin-inline, auto)}}p:where(.astro-qrw2erpy){border:1px solid var(--sl-color-orange);padding:.75em 1em;background-color:var(--sl-color-orange-low);color:var(--sl-color-orange-high);width:-moz-max-content;width:max-content;max-width:100%;align-items:center;gap:.75em;font-size:var(--sl-text-body-sm);line-height:var(--sl-line-height-headings)}a:where(.astro-er6lgoxi){gap:.5rem;align-items:center;text-decoration:none;color:var(--sl-color-gray-3)}a:where(.astro-er6lgoxi):hover{color:var(--sl-color-white)}.pagination-links:where(.astro-zhm7xy73){display:grid;grid-template-columns:repeat(auto-fit,minmax(min(18rem,100%),1fr));gap:1rem}a:where(.astro-zhm7xy73){display:flex;align-items:center;justify-content:flex-start;gap:.5rem;width:100%;flex-basis:calc(50% - .5rem);flex-grow:1;border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding:1rem;text-decoration:none;color:var(--sl-color-gray-2);box-shadow:var(--sl-shadow-md);overflow-wrap:anywhere}:where(.astro-zhm7xy73)[rel=next]{justify-content:end;text-align:end;flex-direction:row-reverse}a:where(.astro-zhm7xy73):hover{border-color:var(--sl-color-gray-2)}.link-title:where(.astro-zhm7xy73){color:var(--sl-color-white);font-size:var(--sl-text-2xl);line-height:var(--sl-line-height-headings)}svg:where(.astro-zhm7xy73){flex-shrink:0}footer:where(.astro-si3zdxsl){flex-direction:column;gap:1.5rem}.meta:where(.astro-si3zdxsl){gap:.75rem 3rem;justify-content:space-between;flex-wrap:wrap;margin-top:3rem;font-size:var(--sl-text-sm);color:var(--sl-color-gray-3)}.meta:where(.astro-si3zdxsl)>p:only-child{margin-inline-start:auto}.kudos:where(.astro-si3zdxsl){align-items:center;gap:.5em;margin:1.5rem auto;font-size:var(--sl-text-xs);text-decoration:none;color:var(--sl-color-gray-3)}.kudos:where(.astro-si3zdxsl) svg{color:var(--sl-color-orange)}.kudos:where(.astro-si3zdxsl):hover{color:var(--sl-color-white)}label:where(.astro-veescumy){--sl-label-icon-size: .875rem;--sl-caret-size: 1.25rem;--sl-inline-padding: .5rem;position:relative;display:flex;align-items:center;gap:.25rem;color:var(--sl-color-gray-1)}label:where(.astro-veescumy):hover{color:var(--sl-color-gray-2)}.icon:where(.astro-veescumy){position:absolute;top:50%;transform:translateY(-50%);pointer-events:none}.label-icon:where(.astro-veescumy){font-size:var(--sl-label-icon-size);inset-inline-start:0}.caret:where(.astro-veescumy){font-size:var(--sl-caret-size);inset-inline-end:0}select:where(.astro-veescumy){border:0;padding-block:.625rem;padding-inline:calc(var(--sl-label-icon-size) + var(--sl-inline-padding) + .25rem) calc(var(--sl-caret-size) + var(--sl-inline-padding) + .25rem);margin-inline:calc(var(--sl-inline-padding) * -1);width:calc(var(--sl-select-width) + var(--sl-inline-padding) * 2);background-color:transparent;text-overflow:ellipsis;color:inherit;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}option:where(.astro-veescumy){background-color:var(--sl-color-bg-nav);color:var(--sl-color-gray-1)}@media (min-width: 50rem){select:where(.astro-veescumy){font-size:var(--sl-text-sm)}}/*! @docsearch/css Modal 3.6.0 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::-moz-placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}.DocSearch-VisuallyHiddenForAccessibility{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}:root{--docsearch-primary-color: var(--sl-color-text-accent);--docsearch-text-color: var(--sl-color-text);--docsearch-spacing: 12px;--docsearch-icon-stroke-width: 1.4;--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-muted-color: var(--sl-color-gray-3);--docsearch-container-background: var(--sl-color-backdrop-overlay);--docsearch-modal-width: 560px;--docsearch-modal-height: 600px;--docsearch-modal-background: var(--sl-color-gray-6);--docsearch-modal-shadow: var(--sl-shadow-lg);--docsearch-searchbox-height: 56px;--docsearch-searchbox-background: var(--sl-color-gray-7, var(--sl-color-gray-6));--docsearch-searchbox-focus-background: var(--sl-color-black);--docsearch-searchbox-shadow: inset 0 0 0 1px var(--docsearch-primary-color);--docsearch-hit-height: 56px;--docsearch-hit-color: var(--sl-color-white);--docsearch-hit-active-color: var(--sl-color-black);--docsearch-hit-background: var(--sl-color-black);--docsearch-key-gradient: linear-gradient( var(--sl-color-bg-inline-code) 0%, var(--sl-color-bg-inline-code) 100% );--docsearch-footer-height: 44px;--docsearch-footer-background: var(--sl-color-black);--docsearch-footer-shadow: 0 -1px 0 0 var(--sl-color-hairline-light)}.DocSearch-Modal{border:1px solid var(--sl-color-hairline-light)}.DocSearch-Logo svg *{fill:var(--docsearch-muted-color)}.DocSearch-Button{display:flex;align-items:center;gap:.5rem;border:0;background-color:transparent;color:var(--sl-color-gray-1);cursor:pointer;height:2.5rem;font-size:var(--sl-text-xl)}.DocSearch-Button-Container{display:contents}.DocSearch-Search-Icon{width:.875em;height:.875em;stroke-width:.125rem}.DocSearch-Button-Placeholder,.DocSearch-Button-Keys,.DocSearch-Button-Key{display:none}@media (min-width: 50rem){sl-doc-search{width:100%}.DocSearch-Button{border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding-inline-start:.75rem;padding-inline-end:1rem;background-color:var(--sl-color-black);color:var(--sl-color-gray-2);font-size:var(--sl-text-sm);width:100%;max-width:22rem}.DocSearch-Button:hover{border-color:var(--sl-color-gray-2);color:var(--sl-color-white)}.DocSearch-Button-Placeholder,.DocSearch-Button-Keys{display:flex}.DocSearch-Button-Keys{margin-inline-start:auto}.DocSearch-Button-Keys:before{content:"";width:1em;height:1em;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 2H7a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5Zm3 15a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10Z'%3E%3C/path%3E%3Cpath d='M15.293 6.707a1 1 0 1 1 1.414 1.414l-8.485 8.486a1 1 0 0 1-1.414-1.415l8.485-8.485Z'%3E%3C/path%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 2H7a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5Zm3 15a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10Z'%3E%3C/path%3E%3Cpath d='M15.293 6.707a1 1 0 1 1 1.414 1.414l-8.485 8.486a1 1 0 0 1-1.414-1.415l8.485-8.485Z'%3E%3C/path%3E%3C/svg%3E");-webkit-mask-size:100%;mask-size:100%;background-color:currentColor}}.site-title:where(.astro-lxdxfm6x){align-items:center;gap:var(--sl-nav-gap);font-size:var(--sl-text-h4);font-weight:600;color:var(--sl-color-text-accent);text-decoration:none;white-space:nowrap}img:where(.astro-lxdxfm6x){height:calc(var(--sl-nav-height) - 2 * var(--sl-nav-pad-y));width:auto;max-width:100%;-o-object-fit:contain;object-fit:contain;-o-object-position:0 50%;object-position:0 50%}a:where(.astro-utal4euu){color:var(--sl-color-text-accent);padding:.5em;margin:-.5em}a:where(.astro-utal4euu):hover{opacity:.66}.nav-item:where(.astro-ymhdp2rl){font-size:var(--sl-text-base);font-weight:600;color:var(--sl-color-gray-2);border:1px solid var(--sl-color-text-accent);text-decoration:none;display:flex;align-items:center;border-radius:.25rem;padding:0 1rem}.nav-item:where(.astro-ymhdp2rl):hover{background-color:var(--sl-color-text-accent);color:var(--sl-color-text-invert)}.nav-item:where(.astro-ymhdp2rl).current{background-color:var(--sl-color-white);color:var(--sl-color-text-invert)}.header:where(.astro-rcj4tr4u){gap:var(--sl-nav-gap);justify-content:space-between;align-items:center;height:100%}.title-wrapper:where(.astro-rcj4tr4u){overflow:hidden}.right-group:where(.astro-rcj4tr4u),.social-icons:where(.astro-rcj4tr4u){gap:1rem;align-items:center}.social-icons:where(.astro-rcj4tr4u):after{content:"";height:2rem;border-inline-end:1px solid var(--sl-color-gray-5)}@media (min-width: 50rem){:root[data-has-sidebar]{--__sidebar-pad: calc(2 * var(--sl-nav-pad-x))}:root:not([data-has-toc]){--__toc-width: 0rem}.header:where(.astro-rcj4tr4u){--__sidebar-width: max(0rem, var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x));--__main-column-fr: calc( ( 100% + var(--__sidebar-pad, 0rem) - var(--__toc-width, var(--sl-sidebar-width)) - (2 * var(--__toc-width, var(--sl-nav-pad-x))) - var(--sl-content-inline-start, 0rem) - var(--sl-content-width) ) / 2 );display:grid;grid-template-columns:minmax(calc(var(--__sidebar-width) + max(0rem,var(--__main-column-fr) - var(--sl-nav-gap))),auto) 1fr auto;align-content:center}}.hero:where(.astro-c4rbmq7z){display:grid;align-items:center;gap:1rem;padding-bottom:1rem}.hero:where(.astro-c4rbmq7z)>img:where(.astro-c4rbmq7z),.hero:where(.astro-c4rbmq7z)>.hero-html:where(.astro-c4rbmq7z){-o-object-fit:contain;object-fit:contain;width:min(70%,20rem);height:auto;margin-inline:auto}.stack:where(.astro-c4rbmq7z){flex-direction:column;gap:clamp(1.5rem,calc(1.5rem + 1vw),2rem);text-align:center}.copy:where(.astro-c4rbmq7z){flex-direction:column;gap:1rem;align-items:center}.copy:where(.astro-c4rbmq7z)>:where(.astro-c4rbmq7z){max-width:50ch}h1:where(.astro-c4rbmq7z){font-size:clamp(var(--sl-text-3xl),calc(.25rem + 5vw),var(--sl-text-6xl));line-height:var(--sl-line-height-headings);font-weight:600;color:var(--sl-color-white)}.tagline:where(.astro-c4rbmq7z){font-size:clamp(var(--sl-text-base),calc(.0625rem + 2vw),var(--sl-text-xl));color:var(--sl-color-gray-2)}.actions:where(.astro-c4rbmq7z){gap:1rem 2rem;flex-wrap:wrap;justify-content:center}@media (min-width: 50rem){.hero:where(.astro-c4rbmq7z){grid-template-columns:7fr 4fr;gap:3%;padding-block:clamp(2.5rem,calc(1rem + 10vmin),10rem)}.hero:where(.astro-c4rbmq7z)>img:where(.astro-c4rbmq7z),.hero:where(.astro-c4rbmq7z)>.hero-html:where(.astro-c4rbmq7z){order:2;width:min(100%,25rem)}.stack:where(.astro-c4rbmq7z){text-align:start}.copy:where(.astro-c4rbmq7z){align-items:flex-start}.actions:where(.astro-c4rbmq7z){justify-content:flex-start}}.sl-markdown-content :not(a,strong,em,del,span,input,code)+:not(a,strong,em,del,span,input,code,:where(.not-content *)){margin-top:1rem}.sl-markdown-content :not(h1,h2,h3,h4,h5,h6)+:is(h1,h2,h3,h4,h5,h6):not(:where(.not-content *)){margin-top:1.5em}.sl-markdown-content li+li:not(:where(.not-content *)),.sl-markdown-content dt+dt:not(:where(.not-content *)),.sl-markdown-content dt+dd:not(:where(.not-content *)),.sl-markdown-content dd+dd:not(:where(.not-content *)){margin-top:.25rem}.sl-markdown-content li:not(:where(.not-content *)){overflow-wrap:anywhere}.sl-markdown-content li>:last-child:not(li,ul,ol):not(a,strong,em,del,span,input,:where(.not-content *)){margin-bottom:1.25rem}.sl-markdown-content dt:not(:where(.not-content *)){font-weight:700}.sl-markdown-content dd:not(:where(.not-content *)){padding-inline-start:1rem}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6):not(:where(.not-content *)){color:var(--sl-color-white);line-height:var(--sl-line-height-headings);font-weight:600}.sl-markdown-content :is(img,picture,video,canvas,svg,iframe):not(:where(.not-content *)){display:block;max-width:100%;height:auto}.sl-markdown-content h1:not(:where(.not-content *)){font-size:var(--sl-text-h1)}.sl-markdown-content h2:not(:where(.not-content *)){font-size:var(--sl-text-h2)}.sl-markdown-content h3:not(:where(.not-content *)){font-size:var(--sl-text-h3)}.sl-markdown-content h4:not(:where(.not-content *)){font-size:var(--sl-text-h4)}.sl-markdown-content h5:not(:where(.not-content *)){font-size:var(--sl-text-h5)}.sl-markdown-content h6:not(:where(.not-content *)){font-size:var(--sl-text-h6)}.sl-markdown-content a:not(:where(.not-content *)){color:var(--sl-color-text-accent)}.sl-markdown-content a:hover:not(:where(.not-content *)){color:var(--sl-color-white)}.sl-markdown-content code:not(:where(.not-content *)){background-color:var(--sl-color-bg-inline-code);margin-block:-.125rem;padding:.125rem .375rem;font-size:var(--sl-text-code-sm)}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6) code{font-size:inherit}.sl-markdown-content pre:not(:where(.not-content *)){border:1px solid var(--sl-color-gray-5);padding:.75rem 1rem;font-size:var(--sl-text-code);-moz-tab-size:2;-o-tab-size:2;tab-size:2}.sl-markdown-content pre code:not(:where(.not-content *)){all:unset;font-family:var(--__sl-font-mono)}.sl-markdown-content blockquote:not(:where(.not-content *)){border-inline-start:1px solid var(--sl-color-gray-5);padding-inline-start:1rem}.sl-markdown-content table:not(:where(.not-content *)){display:block;overflow:auto;border-spacing:0}.sl-markdown-content :is(th,td):not(:where(.not-content *)){border-bottom:1px solid var(--sl-color-gray-5);padding:.5rem 1rem;vertical-align:baseline}.sl-markdown-content :is(th:first-child,td:first-child):not(:where(.not-content *)){padding-inline-start:0}.sl-markdown-content :is(th:last-child,td:last-child):not(:where(.not-content *)){padding-inline-end:0}.sl-markdown-content th:not(:where(.not-content *)){color:var(--sl-color-white);font-weight:600}.sl-markdown-content th:not([align]):not(:where(.not-content *)){text-align:start}.sl-markdown-content .starlight-aside :is(th,td,hr,blockquote):not(:where(.not-content *)){border-color:var(--sl-color-gray-4)}@supports (border-color: color-mix(in srgb,var(--sl-color-asides-text-accent) 30%,transparent)){.sl-markdown-content .starlight-aside :is(th,td,hr,blockquote):not(:where(.not-content *)){border-color:color-mix(in srgb,var(--sl-color-asides-text-accent) 30%,transparent)}}@supports (border-color: color-mix(in srgb,var(--sl-color-asides-text-accent) 12%,transparent)){.sl-markdown-content .starlight-aside code:not(:where(.not-content *)){background-color:color-mix(in srgb,var(--sl-color-asides-text-accent) 12%,transparent)}}.sl-markdown-content hr:not(:where(.not-content *)){border:0;border-bottom:1px solid var(--sl-color-hairline)}.sl-markdown-content details:not(:where(.not-content *)){--sl-details-border-color: var(--sl-color-gray-5);--sl-details-border-color--hover: var(--sl-color-text-accent);border-inline-start:2px solid var(--sl-details-border-color);padding-inline-start:1rem}.sl-markdown-content details:not([open]):hover:not(:where(.not-content *)),.sl-markdown-content details:has(>summary:hover):not(:where(.not-content *)){border-color:var(--sl-details-border-color--hover)}.sl-markdown-content summary:not(:where(.not-content *)){color:var(--sl-color-white);cursor:pointer;display:block;font-weight:600;margin-inline-start:-.5rem;padding-inline-start:.5rem}.sl-markdown-content details[open]>summary:not(:where(.not-content *)){margin-bottom:1rem}.sl-markdown-content summary:not(:where(.not-content *))::marker,.sl-markdown-content summary:not(:where(.not-content *))::-webkit-details-marker{display:none}.sl-markdown-content summary:not(:where(.not-content *)):before{--sl-details-marker-size: 1.25rem;background-color:currentColor;content:"";display:inline-block;height:var(--sl-details-marker-size);width:var(--sl-details-marker-size);margin-inline:calc((var(--sl-details-marker-size) / 4) * -1) .25rem;vertical-align:middle;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}@media (prefers-reduced-motion: no-preference){.sl-markdown-content summary:not(:where(.not-content *)):before{transition:transform .2s ease-in-out}}.sl-markdown-content details[open]>summary:not(:where(.not-content *)):before{transform:rotate(90deg)}[dir=rtl] .sl-markdown-content summary:not(:where(.not-content *)):before,.sl-markdown-content [dir=rtl] summary:not(:where(.not-content *)):before{transform:rotate(180deg)}.sl-markdown-content summary:not(:where(.not-content *)) p:only-child{display:inline}.sl-markdown-content .starlight-aside details:not(:where(.not-content *)){--sl-details-border-color: var(--sl-color-asides-border);--sl-details-border-color--hover: var(--sl-color-asides-text-accent)}[data-mobile-menu-expanded]{overflow:hidden}@media (min-width: 50rem){[data-mobile-menu-expanded]{overflow:auto}}button:where(.astro-zubfvqmm){position:fixed;top:calc((var(--sl-nav-height) - var(--sl-menu-button-size)) / 2);inset-inline-end:var(--sl-nav-pad-x);z-index:var(--sl-z-index-navbar);border:0;border-radius:50%;width:var(--sl-menu-button-size);height:var(--sl-menu-button-size);padding:.5rem;background-color:var(--sl-color-white);color:var(--sl-color-black);box-shadow:var(--sl-shadow-md);cursor:pointer}:where(.astro-zubfvqmm)[aria-expanded=true] button:where(.astro-zubfvqmm){background-color:var(--sl-color-gray-2);box-shadow:none}[data-theme=light] button:where(.astro-zubfvqmm){background-color:var(--sl-color-black);color:var(--sl-color-white)}[data-theme=light] :where(.astro-zubfvqmm)[aria-expanded=true] button:where(.astro-zubfvqmm){background-color:var(--sl-color-gray-5)}.page:where(.astro-fsz2uium){flex-direction:column;min-height:100vh}.header:where(.astro-fsz2uium){z-index:var(--sl-z-index-navbar);position:fixed;inset-inline-start:0;inset-block-start:0;width:100%;height:var(--sl-nav-height);border-bottom:1px solid var(--sl-color-hairline-shade);padding:var(--sl-nav-pad-y) var(--sl-nav-pad-x);padding-inline-end:var(--sl-nav-pad-x);background-color:var(--sl-color-bg-nav)}[data-has-sidebar] .header:where(.astro-fsz2uium){padding-inline-end:calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size))}.sidebar-pane:where(.astro-fsz2uium){visibility:var(--sl-sidebar-visibility, hidden);position:fixed;z-index:var(--sl-z-index-menu);inset-block:var(--sl-nav-height) 0;inset-inline-start:0;width:100%;background-color:var(--sl-color-black);overflow-y:auto}[aria-expanded=true]~.sidebar-pane:where(.astro-fsz2uium){--sl-sidebar-visibility: visible}.sidebar-content:where(.astro-fsz2uium){height:100%;min-height:-moz-max-content;min-height:max-content;padding:1rem var(--sl-sidebar-pad-x) 0;flex-direction:column;gap:1rem}@media (min-width: 50rem){.sidebar-content:where(.astro-fsz2uium):after{content:"";padding-bottom:1px}}.main-frame:where(.astro-fsz2uium){padding-top:calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));padding-inline-start:var(--sl-content-inline-start)}@media (min-width: 50rem){[data-has-sidebar] .header:where(.astro-fsz2uium){padding-inline-end:var(--sl-nav-pad-x)}.sidebar-pane:where(.astro-fsz2uium){--sl-sidebar-visibility: visible;width:var(--sl-sidebar-width);background-color:var(--sl-color-bg-sidebar);border-inline-end:1px solid var(--sl-color-hairline-shade)}}ul:where(.astro-56nbxl5q){padding:0;list-style:none}a:where(.astro-56nbxl5q){--pad-inline: .5rem;display:block;border-radius:.25rem;padding-block:.25rem;padding-inline:calc(1rem * var(--depth) + var(--pad-inline)) var(--pad-inline);line-height:1.25}a:where(.astro-56nbxl5q)[aria-current=true]{color:var(--sl-color-text-accent)}.isMobile:where(.astro-56nbxl5q) a:where(.astro-56nbxl5q){--pad-inline: 1rem;display:flex;justify-content:space-between;gap:var(--pad-inline);border-top:1px solid var(--sl-color-gray-6);border-radius:0;padding-block:.5rem;color:var(--sl-color-text);font-size:var(--sl-text-sm);text-decoration:none;outline-offset:var(--sl-outline-offset-inside)}.isMobile:where(.astro-56nbxl5q):first-child>li:where(.astro-56nbxl5q):first-child>a:where(.astro-56nbxl5q){border-top:0}.isMobile:where(.astro-56nbxl5q) a:where(.astro-56nbxl5q)[aria-current=true],.isMobile:where(.astro-56nbxl5q) a:where(.astro-56nbxl5q)[aria-current=true]:hover,.isMobile:where(.astro-56nbxl5q) a:where(.astro-56nbxl5q)[aria-current=true]:focus{color:var(--sl-color-white);background-color:unset}.isMobile:where(.astro-56nbxl5q) a:where(.astro-56nbxl5q)[aria-current=true]:after{content:"";width:1rem;background-color:var(--sl-color-text-accent);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;flex-shrink:0}nav:where(.astro-bezzg4pa){position:fixed;z-index:var(--sl-z-index-toc);top:calc(var(--sl-nav-height) - 1px);inset-inline:0;border-top:1px solid var(--sl-color-gray-5);background-color:var(--sl-color-bg-nav)}@media (min-width: 50rem){nav:where(.astro-bezzg4pa){inset-inline-start:var(--sl-content-inline-start, 0)}}summary:where(.astro-bezzg4pa){gap:.5rem;align-items:center;height:var(--sl-mobile-toc-height);border-bottom:1px solid var(--sl-color-hairline-shade);padding:.5rem 1rem;font-size:var(--sl-text-xs);outline-offset:var(--sl-outline-offset-inside)}summary:where(.astro-bezzg4pa)::marker,summary:where(.astro-bezzg4pa)::-webkit-details-marker{display:none}.toggle:where(.astro-bezzg4pa){flex-shrink:0;gap:1rem;align-items:center;justify-content:space-between;border:1px solid var(--sl-color-gray-5);border-radius:.5rem;padding-block:.5rem;padding-inline-start:.75rem;padding-inline-end:.5rem;line-height:1;background-color:var(--sl-color-black);-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer}details:where(.astro-bezzg4pa)[open] .toggle:where(.astro-bezzg4pa){color:var(--sl-color-white);border-color:var(--sl-color-accent)}details:where(.astro-bezzg4pa) .toggle:where(.astro-bezzg4pa):hover{color:var(--sl-color-white);border-color:var(--sl-color-gray-2)}[dir=rtl] .caret:where(.astro-bezzg4pa){transform:rotate(180deg)}details:where(.astro-bezzg4pa)[open] .caret:where(.astro-bezzg4pa){transform:rotate(90deg)}.display-current:where(.astro-bezzg4pa){white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:var(--sl-color-white)}.dropdown:where(.astro-bezzg4pa){--border-top: 1px;margin-top:calc(-1 * var(--border-top));border:var(--border-top) solid var(--sl-color-gray-6);border-top-color:var(--sl-color-hairline-shade);max-height:calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));overflow-y:auto;background-color:var(--sl-color-black);box-shadow:var(--sl-shadow-md);overscroll-behavior:contain}.right-sidebar-panel:where(.astro-uie4kptu){padding:1rem var(--sl-sidebar-pad-x)}.sl-container:where(.astro-uie4kptu){width:calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x))}.right-sidebar-panel:where(.astro-uie4kptu) h2{color:var(--sl-color-white);font-size:var(--sl-text-h5);font-weight:600;line-height:var(--sl-line-height-headings);margin-bottom:.5rem}.right-sidebar-panel:where(.astro-uie4kptu) :where(a){display:block;font-size:var(--sl-text-xs);text-decoration:none;color:var(--sl-color-gray-3);overflow-wrap:anywhere}.right-sidebar-panel:where(.astro-uie4kptu) :where(a):hover{color:var(--sl-color-white)}@media (min-width: 72rem){.sl-container:where(.astro-uie4kptu){max-width:calc(((100vw - var(--sl-sidebar-width) - 2 * var(--sl-content-pad-x) - 2 * var(--sl-sidebar-pad-x)) * .25))}}h1:where(.astro-nllaikn6){margin-top:1rem;font-size:var(--sl-text-h1);line-height:var(--sl-line-height-headings);font-weight:600;color:var(--sl-color-white)}.social-icons:where(.astro-jwiyop76){margin-inline-end:auto;gap:1rem;align-items:center;padding-block:1rem}.social-icons:where(.astro-jwiyop76):empty{display:none}.mobile-preferences:where(.astro-jwiyop76){justify-content:space-between;flex-wrap:wrap;border-top:1px solid var(--sl-color-gray-6);-moz-column-gap:1rem;column-gap:1rem;padding:.5rem 0}sl-sidebar-state-persist:where(.astro-gqlmpswf){display:contents}ul:where(.astro-c4v4odsm){--sl-sidebar-item-padding-inline: .5rem;list-style:none;padding:0}li:where(.astro-c4v4odsm){overflow-wrap:anywhere}ul:where(.astro-c4v4odsm) ul:where(.astro-c4v4odsm) li:where(.astro-c4v4odsm){margin-inline-start:var(--sl-sidebar-item-padding-inline);border-inline-start:1px solid var(--sl-color-hairline-light);padding-inline-start:var(--sl-sidebar-item-padding-inline)}.large:where(.astro-c4v4odsm){font-size:var(--sl-text-lg);font-weight:600;color:var(--sl-color-white)}.top-level:where(.astro-c4v4odsm)>li:where(.astro-c4v4odsm)+li:where(.astro-c4v4odsm){margin-top:.75rem}summary:where(.astro-c4v4odsm){display:flex;align-items:center;justify-content:space-between;padding:.2em var(--sl-sidebar-item-padding-inline);line-height:1.4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}summary:where(.astro-c4v4odsm)::marker,summary:where(.astro-c4v4odsm)::-webkit-details-marker{display:none}.caret:where(.astro-c4v4odsm){transition:transform .2s ease-in-out;flex-shrink:0}[dir=rtl] .caret:where(.astro-c4v4odsm){transform:rotate(180deg)}:where(.astro-c4v4odsm)[open]>summary:where(.astro-c4v4odsm) .caret:where(.astro-c4v4odsm){transform:rotate(90deg)}a:where(.astro-c4v4odsm){display:block;border-radius:.25rem;text-decoration:none;color:var(--sl-color-gray-2);padding:.3em var(--sl-sidebar-item-padding-inline);line-height:1.4}a:where(.astro-c4v4odsm):hover,a:where(.astro-c4v4odsm):focus{color:var(--sl-color-white)}:where(.astro-c4v4odsm)[aria-current=page],:where(.astro-c4v4odsm)[aria-current=page]:hover,:where(.astro-c4v4odsm)[aria-current=page]:focus{font-weight:600;color:var(--sl-color-text-invert);background-color:var(--sl-color-text-accent)}a:where(.astro-c4v4odsm)>:where(.astro-c4v4odsm):not(:last-child),.group-label:where(.astro-c4v4odsm)>:where(.astro-c4v4odsm):not(:last-child){margin-inline-end:.25em}@media (min-width: 50rem){.top-level:where(.astro-c4v4odsm)>li:where(.astro-c4v4odsm)+li:where(.astro-c4v4odsm){margin-top:.5rem}.large:where(.astro-c4v4odsm){font-size:var(--sl-text-base)}a:where(.astro-c4v4odsm){font-size:var(--sl-text-sm)}}a:where(.astro-7o6lzqw7){clip:rect(0,0,0,0);position:fixed;top:.75rem;inset-inline-start:.75rem}a:where(.astro-7o6lzqw7):focus{clip:unset;z-index:var(--sl-z-index-skiplink);display:block;padding:.5rem 1rem;text-decoration:none;color:var(--sl-color-text-invert);background-color:var(--sl-color-text-accent);box-shadow:var(--sl-shadow-lg)}.main-pane:where(.astro-qdwfi2vn){isolation:isolate}@media (min-width: 72rem){.right-sidebar-container:where(.astro-qdwfi2vn){order:2;position:relative;width:calc(var(--sl-sidebar-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2)}.right-sidebar:where(.astro-qdwfi2vn){position:fixed;top:0;border-inline-start:1px solid var(--sl-color-gray-6);padding-top:var(--sl-nav-height);width:100%;height:100vh;overflow-y:auto;scrollbar-width:none}.main-pane:where(.astro-qdwfi2vn){width:100%}[data-has-sidebar][data-has-toc] .main-pane:where(.astro-qdwfi2vn){--sl-content-margin-inline: auto 0;order:1;width:calc(var(--sl-content-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2)}}.starlight-aside{padding:1rem;border-inline-start:.25rem solid var(--sl-color-asides-border);color:var(--sl-color-white)}.starlight-aside--note{--sl-color-asides-text-accent: var(--sl-color-blue-high);--sl-color-asides-border: var(--sl-color-blue);background-color:var(--sl-color-blue-low)}.starlight-aside--tip{--sl-color-asides-text-accent: var(--sl-color-purple-high);--sl-color-asides-border: var(--sl-color-purple);background-color:var(--sl-color-purple-low)}.starlight-aside--caution{--sl-color-asides-text-accent: var(--sl-color-orange-high);--sl-color-asides-border: var(--sl-color-orange);background-color:var(--sl-color-orange-low)}.starlight-aside--danger{--sl-color-asides-text-accent: var(--sl-color-red-high);--sl-color-asides-border: var(--sl-color-red);background-color:var(--sl-color-red-low)}.starlight-aside__title{display:flex;gap:.5rem;align-items:center;font-size:var(--sl-text-h5);font-weight:600;line-height:var(--sl-line-height-headings);color:var(--sl-color-asides-text-accent)}.starlight-aside__icon{font-size:1.333em;width:1em;height:1em}.starlight-aside__title+.starlight-aside__content{margin-top:.5rem}.starlight-aside__content a{color:var(--sl-color-asides-text-accent)}html[data-theme=dark] img[src*=lightModeOnly],html[data-theme=light] img[src*=darkModeOnly]{display:none!important}:root{--sl-content-width: 80rem}*,:before,:after{border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: }html,:host{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}:root{--sl-font: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--sl-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--sl-color-white: #fff;--sl-color-gray-1: #e5e7eb;--sl-color-gray-2: #d1d5db;--sl-color-gray-3: #9ca3af;--sl-color-gray-4: #4b5563;--sl-color-gray-5: #374151;--sl-color-gray-6: #1f2937;--sl-color-black: #111827;--sl-color-accent-low: #1e1b4b;--sl-color-accent: #4f46e5;--sl-color-accent-high: #c7d2fe}:root[data-theme=light]{--sl-color-white: #111827;--sl-color-gray-1: #1f2937;--sl-color-gray-2: #374151;--sl-color-gray-3: #6b7280;--sl-color-gray-4: #9ca3af;--sl-color-gray-5: #d1d5db;--sl-color-gray-6: #e5e7eb;--sl-color-gray-7: #f3f4f6;--sl-color-black: #fff;--sl-color-accent-low: #c7d2fe;--sl-color-accent: #4f46e5;--sl-color-accent-high: #312e81}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.static{position:static}.relative{position:relative}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mt-4{margin-top:1rem}.inline{display:inline}.flex{display:flex}.grid{display:grid}.w-24{width:6rem}.w-full{width:100%}.min-w-24{min-width:6rem}.flex-1{flex:1 1 0%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.py-4{padding-top:1rem;padding-bottom:1rem}.text-center{text-align:center}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media (min-width: 1024px){.lg\:flex-row{flex-direction:row}}.\[\&_\*\]\:\!mt-0 *{margin-top:0!important}html:not([data-has-toc]){--sl-mobile-toc-height: 0rem}html:not([data-has-sidebar]){--sl-content-width: 67.5rem}html{scroll-padding-top:calc(1.5rem + var(--sl-nav-height) + var(--sl-mobile-toc-height))}main:where(.astro-v2lcxxeb){padding-bottom:3vh}@media (min-width: 50em){:where(.astro-v2lcxxeb)[data-has-sidebar]{--sl-content-inline-start: var(--sl-sidebar-width)}}@media (min-width: 72em){html{scroll-padding-top:calc(1.5rem + var(--sl-nav-height))}} diff --git a/next/_astro/logo-dark-small.CNeORTxh_1g9j8I.webp b/next/_astro/logo-dark-small.CNeORTxh_Z21YbU4.webp similarity index 100% rename from next/_astro/logo-dark-small.CNeORTxh_1g9j8I.webp rename to next/_astro/logo-dark-small.CNeORTxh_Z21YbU4.webp diff --git a/next/assets/cross-platform/index.html b/next/assets/cross-platform/index.html index 3702d90d..733c508e 100644 --- a/next/assets/cross-platform/index.html +++ b/next/assets/cross-platform/index.html @@ -1,4 +1,4 @@ - Using Player Across Multiple Platforms | Player - - Skip to content
Skip to content

Using Player Across Multiple Platforms

One of the major benefits of adopting Player for rendering a UI is it’s ability to function across all the platforms Player supports (React, Android, iOS) using the same content. In order to facilitate this, and get the most out of Player’s architecture, here’s a few things to keep in mind as you integrate Player into multiple platforms.

+

Using Player Across Multiple Platforms

One of the major benefits of adopting Player for rendering a UI is it’s ability to function across all the platforms Player supports (React, Android, iOS) using the same content. In order to facilitate this, and get the most out of Player’s architecture, here’s a few things to keep in mind as you integrate Player into multiple platforms.

Use Core Plugins to Share Functionality

One of the easiest, and most beneficial thing to do when integrating Player is to organize feature sets into plugins. Keeping each plugin small and concise allows for easy sharing of features with other Player integrations. Player Core, as well as each of the platform integration, supplies an interface for users to augment/extend the functionality of Player. This can be used to author validations, formatters, expressions, etc once, and be shared across Player implementations across platforms. As Player Core runs on every platform, any core Player plugin will also run on every platform which allows for shared functionality to be authored once and shared across every platform.

-

Plugins such as the common-types plugin is a great example of a plugin used to add a feature to Player in a reusable way. Since the validations, formats, and data-types present in this plugin are authored in Player Core, we eliminate the need to rewrite them to target each individual platform. Using this mechanism, we also guaranteed that the React, iOS, and Android Player configurations are able to process the same content in the same way across the systems. The most important usecase of this is the asset transforms. Writing the logic to transform the asset as authored in content to the UI representation in the shared transform layer moves repeated functionality to the shared layer decreasing the implementation complexity of each asset on every platform but still leaves room for rendering variability for each platform.

+

Plugins such as the common-types plugin is a great example of a plugin used to add a feature to Player in a reusable way. Since the validations, formats, and data-types present in this plugin are authored in Player Core, we eliminate the need to rewrite them to target each individual platform. Using this mechanism, we also guaranteed that the React, iOS, and Android Player configurations are able to process the same content in the same way across the systems. The most important usecase of this is the asset transforms. Writing the logic to transform the asset as authored in content to the UI representation in the shared transform layer moves repeated functionality to the shared layer decreasing the implementation complexity of each asset on every platform but still leaves room for rendering variability for each platform.

Use the meta-plugin to simplify plugin sets

In the previous example, two things were mentioned that may initially seem to contradict each other:

@@ -82,4 +82,4 @@

Use the meta-plugin to simp

features/expressions/validations added in 1 are automatically present in the other platforms without any additional work.

If each plugin implements a single, small feature, how are we able to add new features to each platform without requiring additional work?

-

One pattern that we’ve found to work well is to continue to organize each feature into individual plugins/modules. This allows any application to opt-in to an individual feature, and makes sharing much, much simpler. In addition to this, we also create 1 more plugin using the meta-plugin to wrap all of the application’s feature requirements into 1 plugin. This allows additional plugins to to be added to the meta-plugin and each platform will still only require 1 plugin registered (which makes integration easy) while still allowing the individual pieces to be consumed if desired (which makes extensibility easy)

\ No newline at end of file +

One pattern that we’ve found to work well is to continue to organize each feature into individual plugins/modules. This allows any application to opt-in to an individual feature, and makes sharing much, much simpler. In addition to this, we also create 1 more plugin using the meta-plugin to wrap all of the application’s feature requirements into 1 plugin. This allows additional plugins to to be added to the meta-plugin and each platform will still only require 1 plugin registered (which makes integration easy) while still allowing the individual pieces to be consumed if desired (which makes extensibility easy)

\ No newline at end of file diff --git a/next/assets/custom/index.html b/next/assets/custom/index.html index 8d385992..d2c2de45 100644 --- a/next/assets/custom/index.html +++ b/next/assets/custom/index.html @@ -1,4 +1,4 @@ - Custom Assets | Player - Skip to content
Skip to content

Custom Assets

One of the conscious design decisions we made when building Player was to abstract away the actual asset implementation and open it up for users to bring their own when using Player. This way you can seamlessly integrate Player into your existing experiences and reuse UI assets you may have already built. Below we’ve outlined the way to build custom assets on the various platforms Player supports.

+

Custom Assets

One of the conscious design decisions we made when building Player was to abstract away the actual asset implementation and open it up for users to bring their own when using Player. This way you can seamlessly integrate Player into your existing experiences and reuse UI assets you may have already built. Below we’ve outlined the way to build custom assets on the various platforms Player supports.

Create Your Asset

First and foremost you need to create a component to handle rendering of your asset. Without any form of transforms, the props to the component will be those from the incoming player content. It’s recommended that you attach the id, and any other html properties to the root of the asset’s tree:

const CustomAssetComp = (props) => {
return (
<div id={props.id} style={{ color: "purple" }}>
{props.text}
</div>
);
};

Assuming your authored JSON has a string property named text, this will render that.

Register it Using a Plugin

Now that we have a React component to render our asset, let’s create a plugin to register with Player:

import AssetProviderPlugin from "@player-ui/asset-provider-plugin-react";
+

Create Your Asset

First and foremost you need to create a component to handle rendering of your asset. Without any form of transforms, the props to the component will be those from the incoming player content. It’s recommended that you attach the id, and any other html properties to the root of the asset’s tree:

const CustomAssetComp = (props) => {
return (
<div id={props.id} style={{ color: "purple" }}>
{props.text}
</div>
);
};

Assuming your authored JSON has a string property named text, this will render that.

Register it Using a Plugin

Now that we have a React component to render our asset, let’s create a plugin to register with Player:

import AssetProviderPlugin from "@player-ui/asset-provider-plugin-react";
class CustomAssetPlugin implements ReactPlayerPlugin{
applyReact(reactPlayer) {
new AssetProviderPlugin([['custom', CustomAssetComp]]).applyReact(reactPlayer);
}
}

Typically you register assets by type, but the registry acts by finding the most specific partial object match. This allows you to register more specific implementations for assets of the same type.

Rendering Nested Assets

Often times, assets contain a reference or slot to another asset. For this to function properly, the custom asset needs to defer to the React Player to render the sub-asset. Say for instance we change our custom asset to now support a header property that takes another asset.

Use the ReactAsset Component from the @player-ui/react package with the nested asset as props to dynamically determine the rendering implementation to use:

import { ReactAsset } from "@player-ui/react";
const CustomAssetComp = (props) => {
return (
<div id={props.id} style={{ color: "purple" }}>
{props.header && <ReactAsset {...props.header} />}
{props.text}
</div>
);
};

This would automatically find the appropriate handler for the props.header asset and use that to render.

+
data.run()
data.run("arg1", 2)

WrappedAsset

Last but not least, WrappedAsset represents another asset being defined as a part of this asset. This will be a very common pattern as Player content is intended to be semantic and dynamic. Therefore we need to know that there is an asset in our data, but not what it is, as the implementation is not guaranteed.

struct ActionData: AssetData {
var id: String
var type: String
var label: WrappedAsset
var run: WrappedFunction<Void>
}

Rendering these nested assets will be described below.

View

The view for a SwiftUI Asset is a regular SwiftUI View. Any standard SwiftUI components and concepts will work as normal. The only differentiating factor when it comes to Player assets, is the WrappedAsset and rendering it. WrappedAsset contains a SwiftUIAsset?, so in the event it was not decodable, it will be nil, otherwise, you can access it’s view property to get a type-erased AnyView and render it in your view:

struct ActionView: View {
var body: some View {
Button(action: {}, label: {
if let label = decodedAssetData.label.asset {
label.view
}
})
}
}

Asset

The SwiftUIAsset is the glue between the View and the Data. Player will handle decoding data, and updating the data in an ObservableObject viewModel that contains the Data you tell it to decode.

UncontrolledAsset

The UncontrolledAsset is uncontrolled because you do not specify a viewModel type, and receive an implicit AssetViewModel<T: AssetData>.

class ActionAsset: UncontrolledAsset<ActionData> {
// Populated for you, but copied here for reference
@ObservedObject var model: AssetViewModel<ActionData>
}

ControlledAsset

The ControlledAsset lets you define the viewModel type, as long as it subclasses AssetViewModel<T: AssetData>, this way you still receive updated data and user info whenever Player changes state, but you can add other functionality to the viewModel.

class ActionViewModel: AssetViewModel<ActionData> {
public required init(_ data: ActionData, userInfo: [CodingUserInfoKey: Any]) {
super.init(data, userInfo: userInfo)
}
}
class ActionAsset: ControlledAsset<ActionData, ActionViewModel> {
// Populated for you, but copied here for reference
@ObservedObject var model: ActionViewModel
}

Linking the View

In either situation, your asset implementation needs only to override the view property and return the type erased view you want to use.

class ActionAsset: UncontrolledAsset<ActionData> {
public override var view: AnyView { AnyView(ActionView(model: model)) }
}

Additional Topics

Interacting with the Data Model without a transform

If data needs to be set or retrieved without the use of a transform, the InProgressState is available in an environment object, where the DataController can be accessed, as well as other utilities:

struct SomeView: View {
@Environment(\.inProgressState) var state: InProgressState?
var body: some View {
Button(action: {
state?.controllers?.data.set(["count": 5])
}, label: {...})
}
}

If your experience will be used on multiple platforms, it is not advised to use this method, a transform will ensure the same logic is followed on all 3 platforms and is strongly encouraged.

Registering your Asset

When registering your asset with an AssetRegistry, it can either be registered as a new type, if it is an entirely new construct, or registered as a variant of an existing asset type, to only be rendered under certain conditions.

-
// Using AssetProviderPlugin from '@player-ui/asset-provider-plugin-react'
new AssetProviderPlugin([
// This will register a match on { type: 'example' }
['example', ExampleAsset],
//This will register a match on { type: 'example', metaData: {"role": "someRole"} }
[{ type: 'example', metaData: {"role": "someRole"}}, ExampleAsset])

In the latter case, it is recommended to extend the original asset, so as to avoid boilerplate for data and construction, and just override the render function. If your variant will have additional data decoded that the original asset does not have, you will need to create the whole asset.

Why Would I Register my Asset as a Variant?
    @@ -141,33 +141,33 @@
    Why Would I Register my Asse

    Use Cases

    Below are 3 different use cases for different ways to mix and match the asset and transform registry to simplify the asset implementation

    Use Case 1: Same type with different variants and asset implementations that can share the same transform

    -
    // Using AssetProviderPlugin from '@player-ui/asset-provider-plugin-react'
    new AssetProviderPlugin([
    //This will register a match on { type: 'example' }
    ["example", ExampleAsset],
    //This will register a match on { type: 'example', metaData: {"role": "someRole"} }
    [{ type: "example", metaData: { role: "someRole" } }, ExampleAsset],
    ]);

    If the common InputData fields for the decoded data looks like:

    -

    Taken from the reference asset Input Asset example, see full transform implementation

    export interface InputAsset {
    id: String;
    type: String;
    value: String?;
    }
    -
    export interface TransformedInput extends InputAsset {
    /** A function to commit the new value to the data-model */
    set: (newValue: ValueType) => void;
    /** The `DataType` associated with this asset, for formatting the keyboard */
    dataType?: DataType;
    }
    +

    Taken from the reference asset Input Asset example, see full transform implementation

    export interface InputAsset {
    id: String;
    type: String;
    value: String?;
    }
    +
    export interface TransformedInput extends InputAsset {
    /** A function to commit the new value to the data-model */
    set: (newValue: ValueType) => void;
    /** The `DataType` associated with this asset, for formatting the keyboard */
    dataType?: DataType;
    }

    And we would like to render two different assets based on whether or not “dataType” is present then both InputAsset and DateInputAsset can share the same InputData which can contain a transform (such as the function to perform after input data is set) but show different content for the views such as input accessories like a calender based on the DataType

    Use Case 2: Same type with different variants and asset implementations that don’t share the same transform

    -
    // Using AssetProviderPlugin from '@player-ui/asset-provider-plugin-react'
    new AssetProviderPlugin([
    ['input', InputAsset],
    [{ type: 'input', dataType: "dataType": {"type": "DateType"}}, DateInputAsset]
    ])

    If the common InputData fields for the decoded data looks like:

    -
    export interface InputAsset {
    id: String;
    type: String;
    value: String?;
    }
    +
    export interface InputAsset {
    id: String;
    type: String;
    value: String?;
    }
    export interface DateInputAsset {
    id: String;
    type: String;
    value: String?;
    }
    // Used in the transform function for inputTranform
    export interface TransformedInput extends InputAsset {
    /** A function to commit the new value to the data-model */
    set: (newValue: ValueType) => void;
    }
    // Used in the transform function for inputDateTranform
    export interface TransformedDateInput extends DateInputAsset {
    /** A function to commit the new value to the data-model */
    set: (newValue: ValueType) => void;
    /** The `DataType` associated with this asset, for formatting the keyboard */
    dataType?: DataType;
    }

    In the case where the regular InputAsset and the DateInputAsset should not share the same transform, its possible to target the variant in the transform registration (since transform also use the partial match registry) to specify a different transform when the “dataType” is present for example:

    import { Player } from "@player-ui/player";
    import { AssetTransformPlugin } from "@player-ui/asset-transform-plugin";
    // Add it to Player
    -
    const player = new Player({
    plugins: [
    new AssetTransformPlugin(
    new Registry([
    // Register a match for any input type with a custom transform.
    [{ type: "input" }, inputTransform],
    // Register a match for any input type that has dataType DateType with a custom transform.
    [{ type: "input", dataType: { type: "DateType" } }, dateInputTransform],
    ]),
    ),
    ],
    });
    +
    const player = new Player({
    plugins: [
    new AssetTransformPlugin(
    new Registry([
    // Register a match for any input type with a custom transform.
    [{ type: "input" }, inputTransform],
    // Register a match for any input type that has dataType DateType with a custom transform.
    [{ type: "input", dataType: { type: "DateType" } }, dateInputTransform],
    ]),
    ),
    ],
    });

    Use Case 3: Different type, same asset implementation, different transforms

    -
    // Using AssetProviderPlugin from '@player-ui/asset-provider-plugin-react'
    new AssetProviderPlugin([
    ["choiceA", Choice],
    ["choiceB", Choice],
    ]);

    Its possible to register the same asset implementation to different type names with the same variant, this may be needed if the two types visually look the same but behaviourally is different such as when the choice is clicked “choiceA” does one action but “choiceB” does something else which is defined in the transform

    Since the transform is called on “select” of the WrappedFunction or Invokable<Unit> in the data this doesnt change the ChoiceData (only the values of select function itself change depending on if we get ChoiceA or ChoiceB) which means they can both be registered to ChoiceAsset

    -
    export interface Choice {
    id: String
    type: String
    value: String?
    }
    +
    export interface Choice {
    id: String
    type: String
    value: String?
    }
    export interface TransformedChoice extends Choice {
    /** A function to commit the new value to the data-model */
    select: (newValue: ValueType) => void;
    }

    Create two transforms choiceATransform and choiceBTransform that both return TransformedChoice but have different functions on select. Then in the web transform registration choiceA and choiceB are registered to those different transforms

    import { Player } from "@player-ui/player";
    import { AssetTransformPlugin } from "@player-ui/asset-transform-plugin";
    // Add it to Player
    -
    const player = new Player({
    plugins: [
    new AssetTransformPlugin(
    new Registry([
    // Register a match for any choiceA type with a custom transform.
    [{ type: "choiceA" }, choiceATransform],
    // Register a match for any choiceB type with a custom transform.
    [{ type: "choiceB" }, choiceBTransform],
    ]),
    ),
    ],
    });
    -

    Overall the asset and transform registry gives developers a lot of flexibility for extending and simplifying assets based on given constraints

\ No newline at end of file +
const player = new Player({
plugins: [
new AssetTransformPlugin(
new Registry([
// Register a match for any choiceA type with a custom transform.
[{ type: "choiceA" }, choiceATransform],
// Register a match for any choiceB type with a custom transform.
[{ type: "choiceB" }, choiceBTransform],
]),
),
],
});
+

Overall the asset and transform registry gives developers a lot of flexibility for extending and simplifying assets based on given constraints

\ No newline at end of file diff --git a/next/assets/dsl/index.html b/next/assets/dsl/index.html index d2e76907..8cca944c 100644 --- a/next/assets/dsl/index.html +++ b/next/assets/dsl/index.html @@ -1,4 +1,4 @@ - Writing DSL Components | Player - - Skip to content
Skip to content

Writing DSL Components

In order to take advantage of the auto-completion and validation of TypeScript types, asset libraries can export a component library for content authoring. Creating components isn’t much different than writing a React component for the web. The primative elements uses the react-json-reconciler to create the JSON content tree, with utilities to make it quick and painless to create new asset-components.

+

Writing DSL Components

In order to take advantage of the auto-completion and validation of TypeScript types, asset libraries can export a component library for content authoring. Creating components isn’t much different than writing a React component for the web. The primative elements uses the react-json-reconciler to create the JSON content tree, with utilities to make it quick and painless to create new asset-components.

Creating a Basic Component

The Asset component from the @player-tools/dsl package is the quickest way to create a new component. The Asset component will take all the Asset’s properties and convert them to their equivalent JSON representation when serialized.

In the examples below, we’ll be creating a TSX component for the action asset in our reference set.

@@ -114,4 +114,4 @@

Components with Specially

The first is to modify the type for the commponent. In the above code snippit we are using the Omit type to remove the base exp property from the source type and replacing it with an exp property that expects a ExpressionTemplateInstance which allows an DSL expression to be passed in.

The second is to extract out the exp property from the props and use a property component to manually control how that property will get serialized. This component is exposed by the underlying react-json-reconciler library which also supplies an array, obj and value component to allow full control over more complicated data structures. The @player-tools/dsl package also exposes the toJsonProperties function to process whole non-Asset objects.

View Components

-

For Assets that are intended to be Views, a View component is exported from the @player-tools/dsl package. Its usage is exactly the same as the Asset component, however it correctly handles the serialization of any Crossfield Validations that exist on the View.

\ No newline at end of file +

For Assets that are intended to be Views, a View component is exported from the @player-tools/dsl package. Its usage is exactly the same as the Asset component, however it correctly handles the serialization of any Crossfield Validations that exist on the View.

\ No newline at end of file diff --git a/next/assets/index.html b/next/assets/index.html index ff47ca4a..37bfc02c 100644 --- a/next/assets/index.html +++ b/next/assets/index.html @@ -1,4 +1,4 @@ - Assets | Player - Skip to content
GitHub

Assets

An asset is a generic term given to a semantic bit of information that we wish to convey to the user. Assets are the backbone that make up the content that Player renders. Though there are many different types of assets, they all follow the same basic principles:

  • Assets are uniquely identified within their view
  • Assets are semantically meaningful in and of themselves, not relying on any other asset to have meaning
  • @@ -91,4 +91,4 @@

    Data Interaction

    UI Rendering

    The last step of the puzzle is how the asset is physically displayed to a user. Given the output of the transform, it presents the user with data to display, or an interactive element to capture a response. Unlike the first 2 parts, the rendering is platform dependent and requires an implementation for each platform you’re using Player with.

    Asset Bundles

    -

    In many cases, sets of assets are bundled together and loaded via a single plugin. For the three platforms we support (iOS, Android, and React), a reference set is created as an example. These provide a variety of common types of assets users may end up creating. While they are capable of being used in a production application, they shouldn’t be viewed as a standard set — but merely an example for teams and apps to create their own standard set. To read more about the reference assets check out the reference section.

\ No newline at end of file +

In many cases, sets of assets are bundled together and loaded via a single plugin. For the three platforms we support (iOS, Android, and React), a reference set is created as an example. These provide a variety of common types of assets users may end up creating. While they are capable of being used in a production application, they shouldn’t be viewed as a standard set — but merely an example for teams and apps to create their own standard set. To read more about the reference assets check out the reference section.

\ No newline at end of file diff --git a/next/assets/reference/index.html b/next/assets/reference/index.html index ff00b3c9..5160ef96 100644 --- a/next/assets/reference/index.html +++ b/next/assets/reference/index.html @@ -1,4 +1,4 @@ - Reference Assets | Player - Skip to content
Skip to content

Reference Assets

To help users get started with Player, we have created a minimal set of useable assets for React, iOS and Android. These reference assets showcase the design philosophy we encourage when building your own asset sets. While these components are functional we do not recommend shipping them to production as they have not been tested to be production ready.

+

Reference Assets

To help users get started with Player, we have created a minimal set of useable assets for React, iOS and Android. These reference assets showcase the design philosophy we encourage when building your own asset sets. While these components are functional we do not recommend shipping them to production as they have not been tested to be production ready.

For the React Player, we ship a package (@player-ui/reference-assets-plugin-react) that provides the reference set of assets. Each asset package exposes a Component, type, and optional transform. They also export hooks for easily consuming the transformed component and supplying your own UI for a custom look and feel. These components are all then exposed via a plugin that can be added to Player like so:

import { ReactPlayer } from "@player-ui/react";
import { ReferenceAssetsPlugin } from "@player-ui/reference-assets-plugin-react";
-
const reactPlayer = new ReactPlayer({
plugins: [new ReferenceAssetsPlugin()],
});

Storybook

The reference assets + React player can be viewed in Storybook

\ No newline at end of file +
struct MyApp: View {
@State var result: Result<CompletedState, PlayerError>? = nil
@ObservedObject var viewModel: MyViewModel
var body: some View {
SwiftUIPlayer(
flow: $viewModel.flowString,
plugins: [ReferenceAssetsPlugin()],
result: $result
)
}
}
\ No newline at end of file diff --git a/next/assets/transforms/index.html b/next/assets/transforms/index.html index a8a04eee..71afc30a 100644 --- a/next/assets/transforms/index.html +++ b/next/assets/transforms/index.html @@ -1,4 +1,4 @@ - Transforms | Player - Skip to content
GitHub

Transforms

This guide will walk through a few of the core concepts of assets, transforms and how they come together with the UI to create an experience.

+

You can read more about what an asset is here. In short:

An asset is a generic term given to a semantic bit of information that we wish to convey to the user. Assets are the primitive elements that make up the content that players present as user experiences.

@@ -78,10 +78,10 @@

Assets and the UI

In general, the pipeline for user-content is something like:

Simple Asset Pipeline
-

Player will do minimal processing of each asset in the tree, resolving data in strings, applicability and any other generic processing it can. The processed tree is then sent to the rendering layer, where it is turned into a native component on the system Player is running on.

+

Player will do minimal processing of each asset in the tree, resolving data in strings, applicability and any other generic processing it can. The processed tree is then sent to the rendering layer, where it is turned into a native component on the system Player is running on.

For simple Assets, like text, there’s no need for any asset specific processing. The output of Player is enough to successfully render and display the content to the user.

Asset Without Transform

Transforms

When dealing with user interactions(inputs, selections) or some further data/communication is needed with Player, we expose a mechanism for individual assets to augment Player’s processing with it’s own custom data. By breaking this out into it’s own discrete step (outside of the UI), we allow asset state to exist outside on an individual rendering, and thus sharable across platforms. These augmentations are processed through a stateless function called a transform.

Asset With Transform
-

Transforms can be added through the asset-transform plugin, and they run as part of the core player’s reconciliation step. This improves performance, as the transforms are cached between updates to the view, and re-usable as they can be registered across platforms to share common logic for assets. You can refer to the cross-platform guide on sharing Player logic across multiple platforms.

\ No newline at end of file +

Transforms can be added through the asset-transform plugin, and they run as part of the core player’s reconciliation step. This improves performance, as the transforms are cached between updates to the view, and re-usable as they can be registered across platforms to share common logic for assets. You can refer to the cross-platform guide on sharing Player logic across multiple platforms.

\ No newline at end of file diff --git a/next/content/assets-views/index.html b/next/content/assets-views/index.html index 694f46f6..07a26dbb 100644 --- a/next/content/assets-views/index.html +++ b/next/content/assets-views/index.html @@ -1,4 +1,4 @@ - Assets & Views | Player - - Skip to content
Skip to content

Assets & Views

Assets

+

Assets & Views

Assets

An asset is a generic term given to a semantic bit of information that we wish to convey to the user. Assets are the primitive elements that make up the content Player presents as user experiences. Though there are many different types of assets, they all follow the same basic principles:

  • assets are uniquely identified within their view
  • @@ -109,8 +109,8 @@ } customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -
    import { Asset } from '@player-tools/dsl';
    -
    <Asset id="parent" type="parent">
    <Asset.Label>
    <Asset id="child-asset" type="child">
    </Asset.Label>
    </Asset>
    +
    import { Asset } from '@player-tools/dsl';
    +
    <Asset id="parent" type="parent">
    <Asset.Label>
    <Asset id="child-asset" type="child">
    </Asset.Label>
    </Asset>

    The label of the parent contains a nested asset reference. These are slots that can usually contain any asset type.

    Views

    Views are assets that exist at the top level of the tree. They typically include the navigation actions, a title, or other top-level information.

    @@ -118,8 +118,8 @@

    Views

    Cross-field validation

    The other special property of a view vs. an asset is the addition of a validation property on the view. These contain validation objects that are used for validations crossing multiple fields, and are ran on user navigation rather than data change.

    Example:

    -
    import { View, expression as e, binding as b } from '@player-tools/dsl';
    -
    <View
    type="view"
    validation=[
    {
    type: 'expression',
    ref: b`foo.data.thing1`,
    message: "Both need to equal 100",
    exp: `${b`foo.data.thing`} + ${b`foo.data.thing2`} == 100`,
    }
    ]
    />
    +
    import { View, expression as e, binding as b } from '@player-tools/dsl';
    +
    <View
    type="view"
    validation=[
    {
    type: 'expression',
    ref: b`foo.data.thing1`,
    message: "Both need to equal 100",
    exp: `${b`foo.data.thing`} + ${b`foo.data.thing2`} == 100`,
    }
    ]
    />

    They follow the same guidelines for normal validation references, with the addition of a ref property that points to the binding that this validation is tied to.

    Applicability

    Any object in the tree (including assets) may contain an applicability property. This is an expression that may conditionally show or hide an asset (and all of it’s children) from the view tree. Applicability is dynamically calculated and will automatically update as data changes on the page.

    @@ -138,8 +138,8 @@

    Static v Dynamic Switches

    A dynamicSwitch will always update the applicable case statement whenever data changes. If data is changed while a view is still showing, the switch will be updated to reflect the new case.

    Example

    Anywhere you can place an asset node, a dynamicSwitch or staticSwitch can be placed instead.

    -
    import { Switch, Asset, expression as e, binding as b } from '@player-tools/dsl';
    -
    <Switch>
    <Switch.Case exp={e`${b`name.first`} == 'John'`}>
    <Asset id="name" type="text" value="Yay" />
    </Switch.Case>
    <Switch.Case exp={e`${b`name.first`} == 'Jane'`}>
    <Asset id="name" type="text" value="Nay" />
    </Switch.Case>
    <Switch.Case>
    <Asset id="name" type="text" value="🤷" />
    </Switch.Case>
    <Switch>
    +
    import { Switch, Asset, expression as e, binding as b } from '@player-tools/dsl';
    +
    <Switch>
    <Switch.Case exp={e`${b`name.first`} == 'John'`}>
    <Asset id="name" type="text" value="Yay" />
    </Switch.Case>
    <Switch.Case exp={e`${b`name.first`} == 'Jane'`}>
    <Asset id="name" type="text" value="Nay" />
    </Switch.Case>
    <Switch.Case>
    <Asset id="name" type="text" value="🤷" />
    </Switch.Case>
    <Switch>

    Templates

    Templates provide a way to dynamically create a list of assets, or any object, based on data from the model. All of the templating semantics are removed by the time it reaches an asset’s transform or UI layer.

    Usage

    @@ -178,4 +178,4 @@

    Dynamic and Static Templates

    {
    "asset": {
    "id": "top-level",
    "type": "collection",
    "template": [
    {
    "dynamic": true,
    "data": "list.of.names",
    "output": "values",
    "value": {
    "asset": {
    "id": "value-_index_",
    "type": "text",
    "value": "{{list.of.names._index_}}"
    }
    }
    }
    ]
    }
    }
    model.set([["list.of.names", ["Jain"]]]);
    model.set([["list.of.names", ["Jain", "Erica"]]]);

    Output

    -
    {
    "asset": {
    "id": "top-level",
    "type": "collection",
    "values": [
    {
    "asset": {
    "id": "value-0",
    "type": "text",
    "value": "Jain"
    }
    },
    {
    "asset": {
    "id": "value-1",
    "type": "text",
    "value": "Erica"
    }
    }
    ]
    }
    }
\ No newline at end of file +
{
"asset": {
"id": "top-level",
"type": "collection",
"values": [
{
"asset": {
"id": "value-0",
"type": "text",
"value": "Jain"
}
},
{
"asset": {
"id": "value-1",
"type": "text",
"value": "Erica"
}
}
]
}
}
\ No newline at end of file diff --git a/next/content/data-expressions/index.html b/next/content/data-expressions/index.html index 0af5e1cc..317a1f9b 100644 --- a/next/content/data-expressions/index.html +++ b/next/content/data-expressions/index.html @@ -1,4 +1,4 @@ - Data & Expressions | Player - - Skip to content
Skip to content

Data & Expressions

Data

+

Data & Expressions

Data

Data is central to a lot of the functionality and features of Player. The easiest way to deal with data is to supply it in the initial payload when starting a flow. This will seed the model with data and allow you to easily reference values

Bindings

A binding is a representation of a path within the data-model. In simple terms, it’s a dot (.) separated string showing the path of the properties within the data object.

@@ -110,7 +110,7 @@

Nested Expressions

Expressions

Expressions are callable functions that allow for dynamic behavior of Player and it’s views.

These functions can be used in ACTION nodes in the navigation section, calculated values in a property of an asset, or anywhere else expressions are valid.

-

Check out the Expression Plugin for registering custom functions.

+

Check out the Expression Plugin for registering custom functions.

Using Expressions and Data in a View

Expressions in the view are strings wrapped in: @[ and ]@.

For instance, to call a sum expression you may use:

@@ -125,9 +125,9 @@

Using Expressions for Inli

format expression is used to replace provided value with appropriate format. For instance, to format a number into currency, you may use:

{
"value": "Total amount is: @[ format('12122016', 'currency') ]@"
}
-

To see the list of currently supported format types, check out Common Types Plugin.

+

To see the list of currently supported format types, check out Common Types Plugin.

Built-in Expressions

-

There are a few expressions built into Player. These are pretty basic, so if you’re looking for extend this — check out the Common Expressions Plugin or the Expression Plugin to add more.

+

There are a few expressions built into Player. These are pretty basic, so if you’re looking for extend this — check out the Common Expressions Plugin or the Expression Plugin to add more.

@@ -167,4 +167,4 @@
setDataVal
deleteDataVal
{
"exp": "deleteDataVal('user.name')"
}
conditional
-
{
"value": "It is @[ conditional({{foo.bar}} == 'DAY', 'daytime', 'nighttime') ]@."
}
\ No newline at end of file +
{
"value": "It is @[ conditional({{foo.bar}} == 'DAY', 'daytime', 'nighttime') ]@."
}
\ No newline at end of file diff --git a/next/content/index.html b/next/content/index.html index dde515c7..53fdaddb 100644 --- a/next/content/index.html +++ b/next/content/index.html @@ -1,4 +1,4 @@ - Content | Player - - Skip to content
Skip to content

Content

Player is driven off of JSON content that describes the user interactions. It mainly consists of a state-machine, some views to drive display, data, and a schema. Player, once started with the JSON content, will play the flow until it reaches a terminal state in the state-machine, and return the outcome, data, and other relevant information about the flow’s execution.

+

Content

Player is driven off of JSON content that describes the user interactions. It mainly consists of a state-machine, some views to drive display, data, and a schema. Player, once started with the JSON content, will play the flow until it reaches a terminal state in the state-machine, and return the outcome, data, and other relevant information about the flow’s execution.

The structure of the JSON payload is described below.

Structure

The high level JSON payload for Player to render consists of: id, views, data, schema, navigation

@@ -79,4 +79,4 @@

Structure

navigation - (required) - The navigation section describes a finite state machine that is responsible driving the core Player experience. The nodes can either be: VIEW, ACTION, EXTERNAL, or FLOW

Example

Below is a minimal example of Player content that loads a view.

-
{
"id": "example-flow",
"views": [
{
"id": "view-1",
"type": "text",
"value": "{{label}}"
}
],
"data": {
"label": "Some Text"
},
"navigation": {
"BEGIN": "FLOW_1",
"FLOW_1": {
"startState": "VIEW_1",
"VIEW_1": {
"state_type": "VIEW",
"ref": "view-1",
"transitions": {}
}
}
}
}
\ No newline at end of file +
{
"id": "example-flow",
"views": [
{
"id": "view-1",
"type": "text",
"value": "{{label}}"
}
],
"data": {
"label": "Some Text"
},
"navigation": {
"BEGIN": "FLOW_1",
"FLOW_1": {
"startState": "VIEW_1",
"VIEW_1": {
"state_type": "VIEW",
"ref": "view-1",
"transitions": {}
}
}
}
}
\ No newline at end of file diff --git a/next/content/navigation/index.html b/next/content/navigation/index.html index 51bb8c15..a6492b59 100644 --- a/next/content/navigation/index.html +++ b/next/content/navigation/index.html @@ -1,4 +1,4 @@ - Navigation | Player - Skip to content
GitHub

Navigation

The navigation section of the content describes the path the user goes through as they progress. In simple terms, this can be thought of as a set of finite state machines, and the user progresses through each state until they hit a DONE node.

The navigation object contains a BEGIN property that contains a reference to a flow to start Player with. Once an end state is reached for that flow, Player will end its processing and return that outcome.

Flows

Flows are a state machine with 1 start state and at least 1 end state. The nodes are processed in order until an end state is reached. The flow that initiated Player (the BEGIN reference) will be executed until its END state is reached.

@@ -117,4 +117,4 @@

Flow with The view node in this flow utilizes an onStart expression to update the ref property dynamically. The expression is evaluated before the data is resolved, and the node references the updated id.

Flow with multiple expression types on a VIEW state

{
"BEGIN": "FLOW_1",
"FLOW_1": {
"startState": "VIEW_1",
"VIEW_1": {
"state_type": "VIEW",
"ref": "view-1",
"onStart": "{{nextState}} = 'VIEW_2'",
"exp": "{{nextState}} = 'VIEW_3'",
"onEnd": "{{nextState}} = 'END_1'",
"transitions": {
"*": "{{nextState}}"
}
},
"VIEW_2": {
"state_type": "VIEW",
"ref": "view-2",
"transitions": {
"*": "END_1"
}
},
"VIEW_3": {
"state_type": "VIEW",
"ref": "view-3",
"transitions": {
"*": "END_1"
}
},
"END_1": {
"state_type": "END",
"outcome": "Done"
}
}
}
-

This flow demonstrates the order of operations for multiple expressions. On transition to VIEW_1 the onStart property’s expression is evaluated first, updating nextState to VIEW_2, followed by the exp updating it to VIEW_3. Right before the next transition the onEnd expression is evaluated; the flow transitions to the END_1 node.

\ No newline at end of file +

This flow demonstrates the order of operations for multiple expressions. On transition to VIEW_1 the onStart property’s expression is evaluated first, updating nextState to VIEW_2, followed by the exp updating it to VIEW_3. Right before the next transition the onEnd expression is evaluated; the flow transitions to the END_1 node.

\ No newline at end of file diff --git a/next/content/schema/index.html b/next/content/schema/index.html index 16af6e26..62a95d66 100644 --- a/next/content/schema/index.html +++ b/next/content/schema/index.html @@ -1,4 +1,4 @@ - Schema | Player - - Skip to content
Skip to content

Schema

The schema section of the content describes the relationship between the view and the data. It breaks down properties of the model’s object into data-types, each with their own spot for validation, formatting, or default-values.

+

Schema

The schema section of the content describes the relationship between the view and the data. It breaks down properties of the model’s object into data-types, each with their own spot for validation, formatting, or default-values.

Structure

The root of the data object uses the ROOT keyword in the schema:

{
"ROOT": {
"myProperty1": {
"type": "MyCustomType"
}
}
}
@@ -78,9 +78,9 @@

Structure

Now the path myProperty1.name in the model points to a StringType. There are a number of basic built-in core types, like StringType, that provide a base set of formatting/validation/default value support out of the box. Read more about those below.

Data Types

A DataType is a collection of the validation rules, formatting, and default values for a given path in the model. See the above structure docs on how to author these in JSON content.

-

As in the above StringType example, there are a number of pre-existing base types that can be utilized in the schema to describe a data type, and an extension mechanism for supplying additional base-types for use in the authored content. See the types-provider-plugin for an easy way to extend these.

+

As in the above StringType example, there are a number of pre-existing base types that can be utilized in the schema to describe a data type, and an extension mechanism for supplying additional base-types for use in the authored content. See the types-provider-plugin for an easy way to extend these.

For any given path described by the schema, Player will merge the rules defined by the base type (if one exists), and the rules defined specifically for that path in the authored content. Any custom formatter/default values take precedence over the default ones, and any custom validations are ran before the base ones.

-

Check out the CommonTypes plugin for a list of easy to consume DataTypes for your application.

+

Check out the CommonTypes plugin for a list of easy to consume DataTypes for your application.

Arrays

Sometimes the data you’re using represents an array or list of things. To indicate this in the schema add a isArray: true property to the reference of the top level array. For example, if the data you’re representing looks like:

{
"pets": [
{
"name": "Frodo",
"type": "cat"
},
{
"name": "Ginger",
"type": "dog"
}
]
}
@@ -90,7 +90,7 @@

Arrays

Validation

To attach a validation to a path in the data-model, add a reference to a validator in the data-type definition for that path under a validation property:

{
"MyCustomType": {
"name": {
"type": "StringType",
"validation": [
{
"type": "length",
"max": 20,
"min": 5
}
]
}
}
}
-

Each validation reference must include a type property which corresponds to the name of the validator to run. Player includes some validators out-of-the-box, and custom validators can be registered as well. See the Common Types Plugin docs for more details around which validators are supported, and how to add custom ones.

+

Each validation reference must include a type property which corresponds to the name of the validator to run. Player includes some validators out-of-the-box, and custom validators can be registered as well. See the Common Types Plugin docs for more details around which validators are supported, and how to add custom ones.

Any additional properties on the validation reference are passed as options to the validator. In the example above, a hypothetical length validator can take a min and max as the boundaries for the length of a string.

Formatting

Similar to adding validations, formatting is added to a data-type through a reference to a formatter.

@@ -98,7 +98,7 @@

Formatting

Just like in validations, the type of the formatter corresponds to the name of a formatter to use (commaNumber in this case). This hypothetical commaNumber formatter would be responsible for translating the data stored in the model to/from what’s displayed to the user. Any additional properties on the format object get passed to the respective formatter function as options. In the above example, the options would include { decimalPlaces: 3 }.

Custom formatters can be registered with Player, and thus exposed for use within the authored content.

The transition from the data stored in the model to the user is considered formating where the inverse (what a user sees to the model is deformatting). Not every formatter has a handler for both formatting and deformatting, and it’s entirely up the formatter to define the requirements for data handling in these scenarios.

-

Read more about formatting here

+

Read more about formatting here

Default Value

Any schema or data type can supply a default value to use when setting or getting the value from the data-model. -Simply supply the default property in either the type reference in the schema or the base type definition. Any read from a binding with a default property will return it’s default if the underlying model’s value is undefined. Note: Reading a value (and it’s default) will not set the value in the model to it’s default. The exception to this rule is for bindings that are tracked for validations.

\ No newline at end of file +Simply supply the default property in either the type reference in the schema or the base type definition. Any read from a binding with a default property will return it’s default if the underlying model’s value is undefined. Note: Reading a value (and it’s default) will not set the value in the model to it’s default. The exception to this rule is for bindings that are tracked for validations.

\ No newline at end of file diff --git a/next/guides/designing-semantic-assets/index.html b/next/guides/designing-semantic-assets/index.html index d3fea80c..26d8141a 100644 --- a/next/guides/designing-semantic-assets/index.html +++ b/next/guides/designing-semantic-assets/index.html @@ -1,4 +1,4 @@ - Designing Semantic Assets | Player - - Skip to content
Skip to content

Designing Semantic Assets

While not a hard requirement by Player, the API design for assets plays an important role in it’s adoption, especially if the intent is to re-use content across platforms. In many cases, Player content is written, and edited many more times than assets are created, and thus it’s schema plays an important role in it’s effective adoption.

-

Player ships with a set of Reference Assets to get started, but intentionally doesn’t include anything beyond some basics. We believe it’s up to each consumer to define their own semantics (if at all), that best suites their applications.

+

Designing Semantic Assets

While not a hard requirement by Player, the API design for assets plays an important role in it’s adoption, especially if the intent is to re-use content across platforms. In many cases, Player content is written, and edited many more times than assets are created, and thus it’s schema plays an important role in it’s effective adoption.

+

Player ships with a set of Reference Assets to get started, but intentionally doesn’t include anything beyond some basics. We believe it’s up to each consumer to define their own semantics (if at all), that best suites their applications.

Intent Based Schema

That being said, building off the learnings from using Player internally, we’ve developed a few guidelines and best practices for how we design asset APIs to help us scale effectively in our applications:

assets are described using their semantic intent regardless of their UI implementation

@@ -81,4 +81,4 @@

Intent Based Schema

options: Array<{
id: string;
value: string;
label: TextAsset;
}>;
}

Here we have a list of choices that a user may select from, the value of which is written to the provided binding. This may take the shape of a radio-group, select, or any number of other rendering options, but the platform specific asset implementations are able to derive the correct rendering without changing the content. The platform implementations may also want to query the asset’s context as additional input into it’s rendering — i.e. if the choice is within a broader form, then render as a select.

-

Using an intent based approach enables content to outlive design changes and the freedom for each platform to choose the best rendering mechanism for itself. It helps to avoid referencing colors/layouts/styles directly but provides additional context to the assets when needed. (Supplying metaData to force a chocie to be compact).

\ No newline at end of file +

Using an intent based approach enables content to outlive design changes and the freedom for each platform to choose the best rendering mechanism for itself. It helps to avoid referencing colors/layouts/styles directly but provides additional context to the assets when needed. (Supplying metaData to force a chocie to be compact).

\ No newline at end of file diff --git a/next/guides/formatting/index.html b/next/guides/formatting/index.html index cc2a3060..5a11aac5 100644 --- a/next/guides/formatting/index.html +++ b/next/guides/formatting/index.html @@ -1,4 +1,4 @@ - Data Formatting and Deformatting | Player - - Skip to content
Skip to content

Data Formatting and Deformatting

Formatting is simply a conversion (or middleware) for getting and setting data. It’s often used to convey a different representation of the data to a user (like displaying a date).

+

Data Formatting and Deformatting

Formatting is simply a conversion (or middleware) for getting and setting data. It’s often used to convey a different representation of the data to a user (like displaying a date).

References

-

In order to attach a specific formatter to a part of the data-model, you need to supply a reference. These can be defined in the schema or a DataType.

+

In order to attach a specific formatter to a part of the data-model, you need to supply a reference. These can be defined in the schema or a DataType.

In either case, the reference to the applicable formatter looks identical:

// An example of a formatter reference for a `date`
{
"type": "date",
"format": "MM/DD/YYYY"
}

The only required property is the type. Any additional properties (format in this example) is passed to the formatter function as options. Not all formatters accept options, and they differ per format type.

@@ -84,7 +84,7 @@

Formatting Lifecycle

In cases where the data-type may change between the user and the data-model (think epoch to a formatted time, or integers to a comma number) you’ll likely want to specify both, though either, both, or neither is also valid.

Using Formatted Data

-

In order to get or set formatted values from the model, add formatted: true to the options of the data-model method. This is most often through the use of a transform.

+

In order to get or set formatted values from the model, add formatted: true to the options of the data-model method. This is most often through the use of a transform.

function assetTransform(value, options) {
return {
...value,
formattedVersion: options.data.model.get('some binding', {
formatted: true,
}),
};
}

This also works for setting data:

options.data.model.set([['some binding', 'some value']], { formatted: true });
@@ -100,4 +100,4 @@

Registering a custom format

Create your format object, and add register it with the plugin:

import { TypesProviderPlugin } from '@player-ui/types-provider-plugin';
const provider = new TypesProviderPlugin({
formats: [
{
name: 'custom-formatter',
format: () => 'formatted-value',
deformat: () => 'deformatted-value',
},
],
});
-

Now any DataType or schema reference to custom-formatter will use this handler.

\ No newline at end of file +

Now any DataType or schema reference to custom-formatter will use this handler.

\ No newline at end of file diff --git a/next/guides/getting-started/index.html b/next/guides/getting-started/index.html index e0177b21..eb4d05b7 100644 --- a/next/guides/getting-started/index.html +++ b/next/guides/getting-started/index.html @@ -1,4 +1,4 @@ - Getting Started | Player - Skip to content
Skip to content

Getting Started

Getting started with Player is simple.

+

Getting Started

Getting started with Player is simple.

Install Dependencies

The first dependency you’ll need to pull in is the Player itself. Additionally, you’ll need an assets plugin to define any UI — we’ll use the reference assets as an example.

+

You can do this by installing the React Player and Reference Assets Packages

Terminal window
npm i @player-ui/react @player-ui/reference-assets-plugin-react
+ +

Configuration

+

Next, in your code you’ll need to initialize Player. This is where you would also initialize any plugins you want to use with Player and create the configuration for Player itself. Below is a minimal example of this.

You can do this by running

Terminal window
yarn add @player-ui/react
yarn add @player-ui/reference-assets-plugin-react

or

Terminal window
npm install @player-ui/react
npm install @player-ui/reference-assets-plugin-react
- -

Configuration

-

Next, in your code you’ll need to initialize Player. This is where you would also initialize any plugins you want to use with Player and create the configuration for Player itself. Below is a minimal example of this.

-
import { ReactPlayer } from "@player-ui/react";
import { ReferenceAssetsPlugin } from "@player-ui/reference-assets-plugin-react";
+
import { ReactPlayer } from "@player-ui/react";
import { ReferenceAssetsPlugin } from "@player-ui/reference-assets-plugin-react";
// create a new web-player instance
-
const reactPlayer = new ReactPlayer({
plugins: [new ReferenceAssetsPlugin()],
});
const reactPlayer = new ReactPlayer({
plugins: [new ReferenceAssetsPlugin()],
});
+

Render Content

-

Now that you have a Player instance created. You’ll need to start it with some content.

-
const content = {
/* your content here */
};
reactPlayer.start(content);

With Player running your content, you’ll need to actually render out what is processes. To do this, you can use the React Player’s component API to insert it into your React tree.

const MyApp = () => {
return <reactPlayer.Component />;
};
\ No newline at end of file +
RuntimesPlatforms
J2V8android, linux, macos
Hermesandroid
GraalJSlinux, macos, win

The HeadlessPlayer does not define a hard dependency on any specific runtime, however, the AndroidPlayer does transitively depends on the j2v8 runtime, as the first class approach. To override, the transitive dependency would need to be explicitly excluded and the actual runtime dependency would need to be added:

dependencies {
// Android Player dependency
implementation("com.intuit.playerui", "android", PLAYER_VERSION) {
// J2V8 included for release versions
exclude(group = "com.intuit.playerui", module = "j2v8-android")
// Debuggable J2V8 included for canary versions
exclude(group = "com.intuit.playerui", module = "j2v8-android-debug")
}
// Override with Hermes runtime for example
implementation("com.intuit.playerui", "hermes-android", PLAYER_VERSION)
}
+

Congrats! You’ve got Player up and running. If you need additional functionality you can add more plugins to extend Player’s functionality. Head over to the Plugins section to take a look at the Plugins we’ve developed or take a look at the Architecture section to see how you can write your own.

\ No newline at end of file diff --git a/next/guides/multi-flow-experiences/index.html b/next/guides/multi-flow-experiences/index.html index 73ed9920..d6b51c51 100644 --- a/next/guides/multi-flow-experiences/index.html +++ b/next/guides/multi-flow-experiences/index.html @@ -1,4 +1,4 @@ - Multi-Flow Experiences | Player - Skip to content
Skip to content

Multi-Flow Experiences

One large use-case for Player involves orchestrating experiences that span multiple screens that may need to communicate with a back-end between stages. This is commonly used for stepped-flows, onboarding workflows, etc, and manifests as using the response from one Player flow to determine the next one. To facilitate this back-and-forth, Player ships with support for creating a flow-manager.

+

Multi-Flow Experiences

One large use-case for Player involves orchestrating experiences that span multiple screens that may need to communicate with a back-end between stages. This is commonly used for stepped-flows, onboarding workflows, etc, and manifests as using the response from one Player flow to determine the next one. To facilitate this back-and-forth, Player ships with support for creating a flow-manager.

Flow Manager

A flow-manager is an interface for asynchronously stepping through a series of flows in a multi-flow experience. Its API mirrors that of the JavaScript iteration protocol; each flow is loaded by calling .next() using the result of the previous flow (if one exists). Implementations are able to leverage this response to retrieve the next flow in the series, or mark the cycle as complete by returning done.

Flow Manager
@@ -108,12 +108,12 @@

Managed Player

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

The ManagedPlayer component from the @player-ui/react module orchestrates running flows through Player using a provided flow-manager. Any provided configuration/plugins will be passed along to the underlying ReactPlayer instance, and React.Suspense is used while awaiting the next flow-manager response.

Simply render the ManagedPlayer with a flow-manager:

import { ManagedPlayer } from "@player-ui/react";
+

The ManagedPlayer component from the @player-ui/react module orchestrates running flows through Player using a provided flow-manager. Any provided configuration/plugins will be passed along to the underlying ReactPlayer instance, and React.Suspense is used while awaiting the next flow-manager response.

Simply render the ManagedPlayer with a flow-manager:

import { ManagedPlayer } from "@player-ui/react";
export const App = () => {
return <ManagedPlayer manager={myFlowManager} />;
};

Callbacks and Error Handling

The ManagedPlayer also includes callbacks for onComplete and onError to handle the completion of a multi-flow experience.

The fallbackComponent is an optional prop that accepts a React component that will be rendered in case of an error. It’s given access to the thrown Error object, as well as a retry and reset callback:

  • retry — Retries the last failed request (the last call to next())
  • reset — Restarts the multi-flow from the begining, calling next() with an empty context.
  • -
\ No newline at end of file +
struct App: View {
var body: some View {
ManagedPlayer(
plugins: [...],
flowManager: myFlowManager,
onComplete: { result in },
fallback: { errorContext in Text("Error!") },
loading: { Text("Loading...") }
)
}
}
\ No newline at end of file diff --git a/next/guides/plugin-implementation/index.html b/next/guides/plugin-implementation/index.html index 2a4e9862..0318c0e6 100644 --- a/next/guides/plugin-implementation/index.html +++ b/next/guides/plugin-implementation/index.html @@ -1,4 +1,4 @@ - Plugin Implementation | Player - - Skip to content
Skip to content

Plugin Implementation

The main purpose of a Plugin is to extend or add new functionality by tapping into Player’s pipeline of components via hooks. In this section we’ll go over the steps to implement a plugin.

-

We’ll use the stage-revert-data plugin as example. After creating our plugin class, we’ll use the apply method which provides access to the Player instance, which then gives access to the necessary hooks.

+

Plugin Implementation

The main purpose of a Plugin is to extend or add new functionality by tapping into Player’s pipeline of components via hooks. In this section we’ll go over the steps to implement a plugin.

+

We’ll use the stage-revert-data plugin as example. After creating our plugin class, we’ll use the apply method which provides access to the Player instance, which then gives access to the necessary hooks.

export default class StageRevertDataPlugin implements PlayerPlugin {
name = 'stage-revert-data-plugin';
apply(player: Player) {
let dataController: DataController;
let stageData: String;
let commitTransitions: String[];
let commitShadowModel: Boolean = false;
const GatedDataMiddleware = new ValidationMiddleware(
() =>
commitShadowModel
? undefined
: {
message: 'staging data',
severity: 'error',
},
{ shouldIncludeInvalid: () => true }
);
@@ -91,4 +91,4 @@
player.hooks.flowController.tap(this.name, (flowController) => {
flowController.hooks.flow.tap(this.name, (flow) => {
flow.hooks.transition.tap(this.name, (from, to) => {
if (from) {
if (commitTransitions.includes(to.name)) {
commitShadowModel = true;
player.logger.debug(
"Shadow Model Data to be committed %s",
GatedDataMiddleware.shadowModelPaths,
);
dataController.set(GatedDataMiddleware.shadowModelPaths);
}
commitShadowModel = false;
GatedDataMiddleware.shadowModelPaths.clear();
}
});
});
});

And this is how we implement a plugin that manages the staging of data based on the view state attributes.

-

Code Snippets Reference: StageRevertDataPlugin

\ No newline at end of file +

Code Snippets Reference: StageRevertDataPlugin

\ No newline at end of file diff --git a/next/guides/writing-plugins/index.html b/next/guides/writing-plugins/index.html index 286c80f6..e165be50 100644 --- a/next/guides/writing-plugins/index.html +++ b/next/guides/writing-plugins/index.html @@ -1,4 +1,4 @@ - Writing a Plugin | Player - Skip to content
Skip to content

Writing a Plugin

While we have published a majority of the plugins we have developed, there will always be new use cases that may require new functionality. Writing a plugin in the easiest way to extend Player functionality for these cases. Plugins work slightly differently on each platform so in this guide we will cover how to write a plugin for each platform.

+

Writing a Plugin

While we have published a majority of the plugins we have developed, there will always be new use cases that may require new functionality. Writing a plugin in the easiest way to extend Player functionality for these cases. Plugins work slightly differently on each platform so in this guide we will cover how to write a plugin for each platform.

Core plugins are the easiest way to extend Player functionality regardless of what platform you are using Player on. To make writing core plugins easy @player-ui/player exposes an interface PlayerPlugin that denotes everything needed. The two mandatory features are a name property which is lets Player know how to refer to the plugin and an implemented apply function that takes a player object. Optionally a symbol property can be used to provide a unique identifier that can be used to retrieve the plugin from Player.

The first step for creating a plugin is making our plugin class, making sure it implements the PlayerPlugin interface from @player-ui/player. By convention, a name attribute with the dash-cased name of your plugin should be defined.

export default class ExamplePlayerPlugin implements PlayerPlugin {
name = 'example-player-plugin';
+

Core plugins are the easiest way to extend Player functionality regardless of what platform you are using Player on. To make writing core plugins easy @player-ui/player exposes an interface PlayerPlugin that denotes everything needed. The two mandatory features are a name property which is lets Player know how to refer to the plugin and an implemented apply function that takes a player object. Optionally a symbol property can be used to provide a unique identifier that can be used to retrieve the plugin from Player.

The first step for creating a plugin is making our plugin class, making sure it implements the PlayerPlugin interface from @player-ui/player. By convention, a name attribute with the dash-cased name of your plugin should be defined.

export default class ExamplePlayerPlugin implements PlayerPlugin {
name = 'example-player-plugin';
{/* A constructor can go here */}
apply(player: Player) {
{/* Your logic here */}
}
-
{/* Helper methods can go here */}
}

The apply function is where the actual logic of the plugin lives. By tapping the hooks exposed via player.hooks you gain access to the internal pipeline of components that comprise Player and can inject your functionality into their exposed hooks. For example if you want to do something any time Player’s state changes you could do the following:

apply(player: Player) {
player.hooks.state.tap(this.name, (state) => {
{/* Your logic here */}
});
}

Some components expose hooks themselves which may require multiple levels of taps which is not uncommon. For example if you wanted to modify the ViewInstance before it was resolved you would do the following:

apply(player: Player) {
player.hooks.viewController.tap(this.name, (vc) => {
vc.hooks.view.tap(this.name, (vc) => {
{/* Your logic here */}
})
});
}

It is not uncommon for core plugins to have constructors for cases where the plugin needs to take some configuration. In cases where plugin configs are more complicated than basic feature flags, it is recommended to make an interface to represent the config object. As an added benefit it also makes it easier to down stream consumers to use your plugin.

For a more comprehensive guide on plugins, check out this Plugin Implementation example.

Note: For the React Player you can import and load the plugin the same way you would a React Player Plugin but for the iOS and Android Players you will need to wrap the javascript bundle in a iOS/Android plugin to ensure it is available on your platform.

{/* Helper methods can go here */}
}

The apply function is where the actual logic of the plugin lives. By tapping the hooks exposed via player.hooks you gain access to the internal pipeline of components that comprise Player and can inject your functionality into their exposed hooks. For example if you want to do something any time Player’s state changes you could do the following:

apply(player: Player) {
player.hooks.state.tap(this.name, (state) => {
{/* Your logic here */}
});
}

Some components expose hooks themselves which may require multiple levels of taps which is not uncommon. For example if you wanted to modify the ViewInstance before it was resolved you would do the following:

apply(player: Player) {
player.hooks.viewController.tap(this.name, (vc) => {
vc.hooks.view.tap(this.name, (vc) => {
{/* Your logic here */}
})
});
}

It is not uncommon for core plugins to have constructors for cases where the plugin needs to take some configuration. In cases where plugin configs are more complicated than basic feature flags, it is recommended to make an interface to represent the config object. As an added benefit it also makes it easier to down stream consumers to use your plugin.

For a more comprehensive guide on plugins, check out this Plugin Implementation example.

Note: For the React Player you can import and load the plugin the same way you would a React Player Plugin but for the iOS and Android Players you will need to wrap the javascript bundle in a iOS/Android plugin to ensure it is available on your platform.

constructor(fn = defaultFunction) {
this.function = fn;
}
applyReact(rp: ReactPlayer) {
rp.hooks.webComponent.tap(this.name, (Comp) => {
const { function } = this;
return () => (
<FunctionContext.Provider value={{ function }}>
<Comp />
</FunctionContext.Provider>
);
});
}
-
{/* Helper methods can go here */}
}

Lastly React plugins can also act as a core plugin in cases where core functionality needs to be extended for the React plugin to work. Since both the PlayerPlugin and ReactPlayerPlugin are typescript interfaces a plugin can implement both and be considered a valid plugin.

{/* Helper methods can go here */}
}

Lastly React plugins can also act as a core plugin in cases where core functionality needs to be extended for the React plugin to work. Since both the PlayerPlugin and ReactPlayerPlugin are typescript interfaces a plugin can implement both and be considered a valid plugin.

\ No newline at end of file +
override open func getArguments() -> [Any] {
// plugin just takes a boolean arguments
return [option]
// More common in JavaScript, constructor takes an object
return [["enable": option]]
}
}

Note: As JavaScriptCore cannot resolve dependencies at runtime, using a module bundler such as tsup is required.

Note: JSBasePlugin implementations do not necessarily need to be a PlayerPlugin, for example, the BeaconPlugin can take plugins in it’s constructor, that are not PlayerPlugin.

\ No newline at end of file diff --git a/next/index.html b/next/index.html index cdf09331..2b6aa314 100644 --- a/next/index.html +++ b/next/index.html @@ -1,4 +1,4 @@ - Player | Player - Skip to content
Skip to content

Player

A cross-platform semantic rendering engine
Player Platform diagram
+

Player

A cross-platform semantic rendering engine
Player Platform diagram

Why Player?

-

Write once, render everywhere

Sharing content across platforms enables you to quickly release new -features, on all platforms, with just a simple content deployment.

Bring your own design system

Player works seamlessly with your existing UI components to fit application. +

Write once, render everywhere

Sharing content across platforms enables you to quickly release new +features, on all platforms, with just a simple content deployment.

Bring your own design system

Player works seamlessly with your existing UI components to fit application. Define your own patterns through assets and render them exactly as your -designers intended.

Server Driven Navigation

Need to figure out where go next? Chaining multiple pages together with -Player is a breeze. Check out the docs for -more details.

Plugin Ready

Player is designed from the ground up with plugins in mind. Read more about the 20+ provided plugins, or how to write your own.

What does it look like?

-
\ No newline at end of file + \ No newline at end of file diff --git a/next/player/about/index.html b/next/player/about/index.html index 82fa310d..5d8e7df4 100644 --- a/next/player/about/index.html +++ b/next/player/about/index.html @@ -1,4 +1,4 @@ - About | Player - Skip to content
GitHub

About

Player logo Player logo

What Is Player?

What is Player? Simply put, Player is a framework that enables users to natively render experiences across multiple platforms via a declarative content payload. At Intuit engineers use Player to accelerate the development of new experiences as Player allows them offload data/state management and fit seamlessly with other applications all with minimal configuration.

@@ -78,4 +78,4 @@

Why Did We Build It?

What Makes It Special?

The first of the big changes with Player was using TypeScript over vanilla JavaScript. By leveraging TypeScript’s strong typing system, we not only made it easier and safer to work on Player, but we were also able to expose our internal types in an easily consumable format. This paves the way for external tooling to be developed around Player.

Now the biggest feature of Player: Plugins. This is discussed more in the Architecture section but at a high level, almost every component of Player exposes entry points to extend its functionality to make it work for your use case. We have included a handful of plugins we’ve developed alongside player but if none of those do what you need, the blueprint is there to build your own.

-

Lastly, Player has iOS and JVM/Android clients. On these platforms you can create native experiences with minimal overhead and not just render a web view. Components can also be written in their native language allow someone with zero web dev experience to use Player.

\ No newline at end of file +

Lastly, Player has iOS and JVM/Android clients. On these platforms you can create native experiences with minimal overhead and not just render a web view. Components can also be written in their native language allow someone with zero web dev experience to use Player.

\ No newline at end of file diff --git a/next/player/faqs/index.html b/next/player/faqs/index.html index a75bb6eb..0cf238e9 100644 --- a/next/player/faqs/index.html +++ b/next/player/faqs/index.html @@ -1,4 +1,4 @@ - FAQs | Player - - Skip to content
Skip to content

FAQs

How much do I need to know about Player to use it?

-

While it definitely helps to understand how it works under the hood, as long as you understand the how Assets & Views, Data & Expressions, and Navigation works you should be ready to author Player content.

-

If you are an engineer integrating Player into your experience you should probably be familiar with what Plugins are available and how to write your own to support the folks who will be authoring the content. The more advanced your use case is you can explore topics as they become applicable.

+

FAQs

How much do I need to know about Player to use it?

+

While it definitely helps to understand how it works under the hood, as long as you understand the how Assets & Views, Data & Expressions, and Navigation works you should be ready to author Player content.

+

If you are an engineer integrating Player into your experience you should probably be familiar with what Plugins are available and how to write your own to support the folks who will be authoring the content. The more advanced your use case is you can explore topics as they become applicable.

Where can I see Player how player is being used?

One of the easiest ways to see Player in action is through our Storybook. This will allow you to take a look at how some of the JSON is passed into player, and what that might look like with our set of reference assets. -If you wanted to get started on using player, please check out our Getting Started Guide.

+If you wanted to get started on using player, please check out our Getting Started Guide.

How is Player versioned?

Player follows semantic versioning. We will also publish changelogs for every release.

    @@ -92,4 +92,4 @@

    I am having issues u

    Contributing

    Check out the Contributing Guide as this will show you all of the requirements to get started using Player, how to contribute, and some platform specific guides.

    I need Player to do _, can we contribute back to Player?

    -

    We love and highly encourage open source contributions! One thing that we ask to keep in mind is that Player is supposed to be generic and flexible. We try to keep any application/implementation specific plugins and assets in a separate codebase. While most functionality can be added though plugins we know that sometimes changes in Player itself might be required. We are definitely open to these changes but take great effort to ensure that there are minimal breaking changes and we aren’t narrowing Player’s functionality.

\ No newline at end of file +

We love and highly encourage open source contributions! One thing that we ask to keep in mind is that Player is supposed to be generic and flexible. We try to keep any application/implementation specific plugins and assets in a separate codebase. While most functionality can be added though plugins we know that sometimes changes in Player itself might be required. We are definitely open to these changes but take great effort to ensure that there are minimal breaking changes and we aren’t narrowing Player’s functionality.

\ No newline at end of file diff --git a/next/player/team/index.html b/next/player/team/index.html index aeb7fb41..a9de4ed2 100644 --- a/next/player/team/index.html +++ b/next/player/team/index.html @@ -1,4 +1,4 @@ - Player Team | Player - - Skip to content
Skip to content

Player Team

Who We Are

+

Player Team

Who We Are

The Player Team is comprised of a group of engineers at Intuit who are passionate about dynamic experiences. Our multidisciplinary background and our multi-platform experience has allowed our team to create and support the Player framework internally for the past few years. You may have seen some of the other open-source projects from out team including auto, design-systems-cli, hooks, proof, and more. We are excited to share our work with the open source community and see how Player grows in the years to come.

The Team

-
Harris Borawski
Harris Borawski
iOS
Adam Dierkens
Adam Dierkens
React, Core
Chloe Han
Chloe Han
React, iOS, Android
Marlon Ercillo
Marlon Ercillo
React
Spencer Hamm
Spencer Hamm
React, Core
Jeremiah Zucker
Jeremiah Zucker
Android
Ketan Reddy
Ketan Reddy
Core, Tools
Nancy Wu
Nancy Wu
iOS
Tony Lin
Tony Lin
Android
Niharika Motukuri
Niharika Motukuri
React, iOS, Android
Rafael Campos
Rafael Campos
Tools
Mrigank Mehta
Mrigank Mehta
React, iOS, Android
\ No newline at end of file +
Chloe Han
Chloe Han
React, iOS, Android
Adam Dierkens
Adam Dierkens
React, Core
Ketan Reddy
Ketan Reddy
Core, Tools
Nancy Wu
Nancy Wu
iOS
Tony Lin
Tony Lin
Android
Niharika Motukuri
Niharika Motukuri
React, iOS, Android
Harris Borawski
Harris Borawski
iOS
Spencer Hamm
Spencer Hamm
React, Core
Marlon Ercillo
Marlon Ercillo
React
Jeremiah Zucker
Jeremiah Zucker
Android
Rafael Campos
Rafael Campos
Tools
Mrigank Mehta
Mrigank Mehta
React, iOS, Android
\ No newline at end of file diff --git a/next/plugins/android/coroutines/index.html b/next/plugins/android/coroutines/index.html index f060748d..e2b0cd09 100644 --- a/next/plugins/android/coroutines/index.html +++ b/next/plugins/android/coroutines/index.html @@ -1,4 +1,4 @@ - Coroutines | Player - Skip to content
Skip to content

Coroutines

Kotlin coroutines is a popular Kotlin library to handle asynchronous programming and concurrency in the JVM. More information on Coroutines can be found here +

Coroutines

Kotlin coroutines is a popular Kotlin library to handle asynchronous programming and concurrency in the JVM. More information on Coroutines can be found here This module contains several JVM exclusive plugins to aid various tooling needs.

UpdatesPlugin

Plugin using a ReceiveChannel to provide an updated asset whenever the Player View is updated.

@@ -102,16 +102,16 @@

Usage

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

In build.gradle

implementation "com.intuit.playerui.plugins:coroutines:$PLAYER_VERSION"

In Player constructor

import com.intuit.playerui.plugins.coroutines.UpdatesPlugin
-
val plugins = listOf(UpdatesPlugin())
AndroidPlayer(plugins)
+

In build.gradle

implementation "com.intuit.playerui.plugins:coroutines:$PLAYER_VERSION"

In Player constructor

import com.intuit.playerui.plugins.coroutines.UpdatesPlugin
+
val plugins = listOf(UpdatesPlugin())
AndroidPlayer(plugins)

API

-

The API for this plugin revolves around the ReceiveChannel for asset updates.
-Public API available from sources here:

UpdatesPlugin

+

The API for this plugin revolves around the ReceiveChannel for asset updates.
+Public API available from sources here:

UpdatesPlugin

FlowScopePlugin

Plugin that provides a CoroutineScope that can be used to perform various async operations with the context of the current flow. All operations launched using the scope will be cancelled when player changes state.

Usage

-

In build.gradle

implementation "com.intuit.playerui.plugins:coroutines:$PLAYER_VERSION"

In Player constructor

import com.intuit.playerui.plugins.coroutines.FlowScopePlugin
-
val plugins = listOf(FlowScopePlugin())
AndroidPlayer(plugins)
+

In build.gradle

implementation "com.intuit.playerui.plugins:coroutines:$PLAYER_VERSION"

In Player constructor

import com.intuit.playerui.plugins.coroutines.FlowScopePlugin
+
val plugins = listOf(FlowScopePlugin())
AndroidPlayer(plugins)

API

-

Public API available from sources here:

FlowScopePlugin

\ No newline at end of file +

Public API available from sources here:

FlowScopePlugin

\ No newline at end of file diff --git a/next/plugins/android/set-timeout/index.html b/next/plugins/android/set-timeout/index.html index c4744909..bd9879f7 100644 --- a/next/plugins/android/set-timeout/index.html +++ b/next/plugins/android/set-timeout/index.html @@ -1,4 +1,4 @@ - SetTimeout Plugin | Player - - Skip to content
Skip to content

SetTimeout Plugin

The only explicit runtime plugin in the core Android plugin set, the SetTimeoutPlugin is a RuntimePlugin written to provide the global setTimeout method to the runtime.

+

SetTimeout Plugin

The only explicit runtime plugin in the core Android plugin set, the SetTimeoutPlugin is a RuntimePlugin written to provide the global setTimeout method to the runtime.

Usage

In build.gradle

implementation "com.intuit.playerui.plugins:set-time-out:$PLAYER_VERSION"

In plugin implementation

class PluginThatNeedsSetTimeout : RuntimePlugin {
override fun apply(runtime: Runtime) {
SetTimeoutPlugin().apply(runtime)
// do other plugin setup
}
-
}
\ No newline at end of file +
}
\ No newline at end of file diff --git a/next/plugins/core/asset-transform/index.html b/next/plugins/core/asset-transform/index.html index 7664c7cb..485f5a9f 100644 --- a/next/plugins/core/asset-transform/index.html +++ b/next/plugins/core/asset-transform/index.html @@ -1,4 +1,4 @@ - Asset Transform | Player - Skip to content
Skip to content

Asset Transform

What are transforms?

+

Asset Transform

What are transforms?

Transforms are functions that map the authored JSON representation of an asset into a stateful JavaScript object, including all of the properties required for that asset to interact with the data-model, navigation, and the rest of Player. This allows UI implementations to have consistent treatment and behavior. While the transform plugin is registered in the core layer, it can be wrapped by the native platforms.

Partial Matching

The transform plugin makes use of the partial-match-registry which ranks matching templates by the number of matching key-value pairs. This allows users to overwrite the implementation of the lookup, if more specific search criteria is provided. This registry/lookup pattern is also utilized in the web-player for the React Component to Asset mapping.

@@ -102,8 +102,8 @@

Usage

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

Transform Arguments

Each transform is a function that is passed 3 arguments: the current asset node, and an options object containing a data-model, expression-evaluator, binding-parser, and a flow transition function, and a store for state management. The transforms should return an immutable representation of the asset, including any means of interacting with the player.

import { Player } from "@player-ui/player";
import { AssetTransformPlugin } from "@player-ui/asset-transform-plugin";
+

Transform Arguments

Each transform is a function that is passed 3 arguments: the current asset node, and an options object containing a data-model, expression-evaluator, binding-parser, and a flow transition function, and a store for state management. The transforms should return an immutable representation of the asset, including any means of interacting with the player.

import { Player } from "@player-ui/player";
import { AssetTransformPlugin } from "@player-ui/asset-transform-plugin";
// Add it to Player
-
const player = new Player({
plugins: [
new AssetTransformPlugin(
new Registry([
// Register a match for any _action_ type with a custom transform.
[
{ type: "action" },
(value) => {
return {
...value,
hello: () => console.log("hello world"),
};
},
],
]),
),
],
});

State Management

Often times node transforms require some state to be stored between updates. Historically this state was put in the data-model under local (to not send it back to the server), but updates became difficult to manage, and namespace collisions under local were up to the transforms to discern.

The third argument to the transform functions expose a store for data, that mirrors the usage of React hooks. This allow transforms to store state information in an automatically scoped store outside of the normal data tree.

A simple transform to manage a counter might look like:

const transform = (asset, options, store) => {
const [count, setCount] = store.useState(0);
return {
...asset,
count,
increment() {
setCount(count + 1);
},
};
};
+
const player = new Player({
plugins: [
new AssetTransformPlugin(
new Registry([
// Register a match for any _action_ type with a custom transform.
[
{ type: "action" },
(value) => {
return {
...value,
hello: () => console.log("hello world"),
};
},
],
]),
),
],
});

State Management

Often times node transforms require some state to be stored between updates. Historically this state was put in the data-model under local (to not send it back to the server), but updates became difficult to manage, and namespace collisions under local were up to the transforms to discern.

The third argument to the transform functions expose a store for data, that mirrors the usage of React hooks. This allow transforms to store state information in an automatically scoped store outside of the normal data tree.

A simple transform to manage a counter might look like:

const transform = (asset, options, store) => {
const [count, setCount] = store.useState(0);
return {
...asset,
count,
increment() {
setCount(count + 1);
},
};
};

Example

-

If the authored JSON is:

{
"actions": [
{
"asset": {
"id": "foo",
"type": "action"
}
}
]
}

the transform-plugin would run the transform on the action asset, attaching a new hello() method. This can be invoked by any consumer of the current view, as a means of interacting with the player.

{
actions: [
{
asset: {
id: "foo",
type: "action",
hello: () => console.log("hello world"),
},
},
];
}
\ No newline at end of file +

If the authored JSON is:

{
"actions": [
{
"asset": {
"id": "foo",
"type": "action"
}
}
]
}

the transform-plugin would run the transform on the action asset, attaching a new hello() method. This can be invoked by any consumer of the current view, as a means of interacting with the player.

{
actions: [
{
asset: {
id: "foo",
type: "action",
hello: () => console.log("hello world"),
},
},
];
}
\ No newline at end of file diff --git a/next/plugins/core/common-expressions/index.html b/next/plugins/core/common-expressions/index.html index 3d110c0c..542b02ce 100644 --- a/next/plugins/core/common-expressions/index.html +++ b/next/plugins/core/common-expressions/index.html @@ -1,4 +1,4 @@ - Common Expressions | Player - Skip to content
Skip to content

Common Expressions

This plugin exposes some basic expressions into Player content.

+

Common Expressions

This plugin exposes some basic expressions into Player content.

It also serves as a good reference to adding your own custom expressions into Player.

Usage

-

Install the plugin:

Terminal window
yarn add @player-ui/common-types-plugin

Add it to Player:

import CommonExpressionsPlugin from "@player-ui/common-expressions-plugin";
+
Terminal window
npm i @player-ui/common-expressions-plugin

Add it to Player:

import { CommonExpressionsPlugin } from '@player-ui/common-expressions-plugin';
const commonExpressionsPlugin = new CommonExpressionsPlugin();
const player = new Player({ plugins: [commonExpressionsPlugin] });
-
// Start your flow
player.start(myFlow);

This will allow any included expressions or custom expressions to be used in the content

+
// Start your flow
player.start(myFlow);

This will allow any included expressions or custom expressions to be used in the content


General

size

@@ -194,4 +194,4 @@

findProperty

Finds the item in the given array that matches the search criteria. Optionally return a specific property or a fallback value if not found

-
function findPropertyIndex(
binding: Binding | Array,
searchProperty: string,
searchValue: any,
fallBackProperty?: string,
fallBackValue?: T,
): T;
\ No newline at end of file +
function findPropertyIndex(
binding: Binding | Array,
searchProperty: string,
searchValue: any,
fallBackProperty?: string,
fallBackValue?: T,
): T;
\ No newline at end of file diff --git a/next/plugins/core/common-types/index.html b/next/plugins/core/common-types/index.html index f5069da5..9fa6ec01 100644 --- a/next/plugins/core/common-types/index.html +++ b/next/plugins/core/common-types/index.html @@ -1,4 +1,4 @@ - Common Types | Player - Skip to content
Skip to content

Common Types

This plugin exposes some basic DataTypes, validations, and formats into Player content.

+

Common Types

This plugin exposes some basic DataTypes, validations, and formats into Player content.

It also serves as a good reference to adding your own custom types into Player.

Usage

-

Install the plugin:

Terminal window
yarn add @player-ui/common-types-plugin

Add it to Player:

import CommonTypesPlugin from "@player-ui/common-types-plugin";
+
Terminal window
npm i @player-ui/common-types-plugin

Add it to Player:

import { CommonTypesPlugin } from '@player-ui/common-types-plugin';
const commonTypesPlugin = new CommonTypesPlugin();
const player = new Player({ plugins: [commonTypesPlugin] });
-
// Start your flow
player.start(myFlow);

This will allow any DataTypes, formats, validations and custom data types to be used in the content

+
// Start your flow
player.start(myFlow);

This will allow any DataTypes, formats, validations and custom data types to be used in the content

Formats

commaNumber

    @@ -359,4 +359,4 @@

    PhoneType

    • validations: phone
    • format: phone ((###) ###-####)
    • -
\ No newline at end of file + \ No newline at end of file diff --git a/next/plugins/core/computed-properties/index.html b/next/plugins/core/computed-properties/index.html index c1d908f6..7c816526 100644 --- a/next/plugins/core/computed-properties/index.html +++ b/next/plugins/core/computed-properties/index.html @@ -1,4 +1,4 @@ - Computed Properties | Player - Skip to content
Skip to content

Computed Properties

This plugin allows users to specify a path in the data-model (binding) as a computed property in the schema. +

Computed Properties

This plugin allows users to specify a path in the data-model (binding) as a computed property in the schema. Anytime this binding is read from, the given expression will be evaluated and returned instead of the it being read from the actual model. Writes to the binding will be prevented, and an error will be thrown.

Usage

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { ComputedPropertiesPlugin } from "@player-ui/computed-properties-plugin";
-
const player = new Player({
plugins: [new ComputedPropertiesPlugin()],
});
+

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { ComputedPropertiesPlugin } from "@player-ui/computed-properties-plugin";
+
const player = new Player({
plugins: [new ComputedPropertiesPlugin()],
});

Expression Data Type

The computed properties plugin introspects the schema, looking for any DataType that uses the Expression:

{
"type": "Expression",
"exp": "@[ someExpression() ]@"
}
@@ -109,4 +109,4 @@

Expression Data Type

Example

{
"schema": {
"ROOT": {
"foo": {
"type:" "FooType"
}
},
"FooType": {
"computedValue": {
"type": "Expression",
"exp": "1 + 2 + 3"
}
}
}
}

Using the above schema, any reference to {{foo.computedValue}} will compute the 1 + 2 + 3 expression and use that as the underlying value for that path.

-

Any write or set operation to {{foo.computedValue}} will result in a thrown exception for writing to a read-only path.

\ No newline at end of file +

Any write or set operation to {{foo.computedValue}} will result in a thrown exception for writing to a read-only path.

\ No newline at end of file diff --git a/next/plugins/core/data-change-listener/index.html b/next/plugins/core/data-change-listener/index.html index 98b176e2..356c8bd1 100644 --- a/next/plugins/core/data-change-listener/index.html +++ b/next/plugins/core/data-change-listener/index.html @@ -1,4 +1,4 @@ - Data Change Listener | Player - Skip to content
Skip to content

Data Change Listener

This plugin enables users to subscribe to data-change events within a view, and run expressions when the target value changes. Expressions are added to a listeners property of the view, with events prefixed by dataChange and the target binding:

+

Data Change Listener

This plugin enables users to subscribe to data-change events within a view, and run expressions when the target value changes. Expressions are added to a listeners property of the view, with events prefixed by dataChange and the target binding:

{
"id": "example-view",
"type": "info",
"listeners": {
"dataChange.foo.bar": "helloWorld()",
"dataChange.foo.baz": ["helloWorld()", "doSomethingElseToo()"]
}
}

Installation

Add it to Player:

import { Player } from "@player-ui/player";
import { DataChangePlugin } from "@player-ui/data-change-plugin";
-
const player = new Player({
plugins: [new DataChangePlugin()],
});
+

Add it to Player:

import { Player } from "@player-ui/player";
import { DataChangePlugin } from "@player-ui/data-change-plugin";
+
const player = new Player({
plugins: [new DataChangePlugin()],
});

Usage

The format of dataChange.<binding> will execute the value (any valid expression or collection of expressions), anytime a value within the target binding’s tree is updated (foo.bar and foo.baz in the example above).

Registrations can be made for any partial binding path, and will be evaluated anytime that path, or any child path, is mutated. The above example registration of dataChange.foo.bar will be triggered by a change to foo.bar, foo.bar.baz, or any other child path. (it will not be triggered by a change to foo.baz).

@@ -110,4 +110,4 @@

Wildcard Placeholders

To subscribe to a binding including a dynamic segment (array index, etc). Use a placeholder value of _ in the binding.

{
"listeners": {
"dataChange.foo._.bar": "helloWorld(_index_)"
}
}

Similar to template processing, any _index_ reference in the expression will be replaced by the dynamic value. In the example above, a change from foo.3.bar will result in the helloWorld() expression being evaluated.

-

Sub-paths that don’t match the changed binding will not trigger the expression. Using the same example, a change from foo.4.baz will not trigger the expression since bar does not match with baz.

\ No newline at end of file +

Sub-paths that don’t match the changed binding will not trigger the expression. Using the same example, a change from foo.4.baz will not trigger the expression since bar does not match with baz.

\ No newline at end of file diff --git a/next/plugins/core/data-filter/index.html b/next/plugins/core/data-filter/index.html index dcbbec09..eb651943 100644 --- a/next/plugins/core/data-filter/index.html +++ b/next/plugins/core/data-filter/index.html @@ -1,4 +1,4 @@ - Data Filter | Player - Skip to content
Skip to content

Data Filter

The data-filter-plugin enables users to filter out segments of the data-model during serialization.

+

Data Filter

The data-filter-plugin enables users to filter out segments of the data-model during serialization.

Usage

Add the plugin to Player and configure the exclusion paths:

import { Player } from "@player-ui/player";
import { DataFilterPlugin } from "@player-ui/data-filter-plugin";
-
const player = new Player({
plugins: [
new DataFilterPlugin({
paths: ["local", "constants"],
}),
],
});

This will exclude any top-level local or constants paths in the data-model from appearing in the serialized response.

\ No newline at end of file +

Add the plugin to Player and configure the exclusion paths:

import { Player } from "@player-ui/player";
import { DataFilterPlugin } from "@player-ui/data-filter-plugin";
+
const player = new Player({
plugins: [
new DataFilterPlugin({
paths: ["local", "constants"],
}),
],
});

This will exclude any top-level local or constants paths in the data-model from appearing in the serialized response.

\ No newline at end of file diff --git a/next/plugins/core/expression/index.html b/next/plugins/core/expression/index.html index 63303fe0..72d0eaf4 100644 --- a/next/plugins/core/expression/index.html +++ b/next/plugins/core/expression/index.html @@ -1,4 +1,4 @@ - Expression | Player - Skip to content
Skip to content

Expression

This plugin assists with exposing custom expressions to Player content.

+

Expression

This plugin assists with exposing custom expressions to Player content.

Usage

Define handlers for the expressions you wish to add:

import {
ExpressionHandler,
ExpressionContext,
} from "@player-ui/expression-plugin";
+

Define handlers for the expressions you wish to add:

import {
ExpressionHandler,
ExpressionContext,
} from "@player-ui/expression-plugin";
const customExpressionHandler: ExpressionHandler = (ctx: ExpressionContext) => {
return "Hello World!";
};

Register with Player. Subsequent registrations of an expression with the same name will override previous values.

import { Player } from "@player-ui/player";
import { ExpressionPlugin } from "@player-ui/expression-plugin";
-
const player = new Player({
plugins: [
new ExpressionPlugin([["myCustomFunction", customExpressionHandler]]),
],
});

Any calls to myCustomFunction() within the flow will utilize the newly registered expression:

{
"asset": {
"id": "sample",
"type": "text",
"value": "@[ myCustomFunction() ]@"
}
}
\ No newline at end of file +
const player = new Player({
plugins: [
new ExpressionPlugin([["myCustomFunction", customExpressionHandler]]),
],
});

Any calls to myCustomFunction() within the flow will utilize the newly registered expression:

{
"asset": {
"id": "sample",
"type": "text",
"value": "@[ myCustomFunction() ]@"
}
}
\ No newline at end of file diff --git a/next/plugins/core/markdown/index.html b/next/plugins/core/markdown/index.html index 99953e6a..807b93a0 100644 --- a/next/plugins/core/markdown/index.html +++ b/next/plugins/core/markdown/index.html @@ -1,4 +1,4 @@ - Markdown | Player - Skip to content
Skip to content

Markdown

The markdown-plugin adds support for parsing markdown content to Player Assets. This plugin is asset set agnostic, so it expects a mappers record to inform how to transform markdown content into valid Player Content with support from your asset set.

+

Markdown

The markdown-plugin adds support for parsing markdown content to Player Assets. This plugin is asset set agnostic, so it expects a mappers record to inform how to transform markdown content into valid Player Content with support from your asset set.

Usage

Defining The Mappers

import type { Mappers } from "@player-ui/markdown-plugin";
+

Defining The Mappers

import type { Mappers } from "@player-ui/markdown-plugin";
export const mappers: Mappers = {
text: ({ originalAsset, value }) => ({
id: `${originalAsset.id}-text`,
type: "text",
value,
}),
image: ({ originalAsset, value, src }) => ({
id: `${originalAsset.id}-image`,
type: "image",
accessibility: value,
metaData: {
ref: src,
},
}),
//...
};

Add the plugin to Player

import { MarkdownPlugin } from "@player-ui/markdown-plugin";
import mappers from "./mappers";
-
const markdownPlugin = new MarkdownPlugin(myMarkdownMappers);
// Add it to the player
const player = new Player({
plugins: [markdownPlugin],
});
\ No newline at end of file +
const markdownPlugin = new MarkdownPlugin(myMarkdownMappers);
// Add it to the player
const player = new Player({
plugins: [markdownPlugin],
});
\ No newline at end of file diff --git a/next/plugins/core/meta/index.html b/next/plugins/core/meta/index.html index 83d65800..02238aaa 100644 --- a/next/plugins/core/meta/index.html +++ b/next/plugins/core/meta/index.html @@ -1,4 +1,4 @@ - Meta | Player - Skip to content
Skip to content

Meta

The Meta Plugin is an easy way to combine multiple other plugins into 1 group. It is often used when sharing a set of plugins across platforms (each platform registering 1 common set of core plugins).

+

Meta

The Meta Plugin is an easy way to combine multiple other plugins into 1 group. It is often used when sharing a set of plugins across platforms (each platform registering 1 common set of core plugins).

Usage

Create a grouping of other plugins:

import { MetaPlugin } from "@player-ui/meta-plugin";
+

Create a grouping of other plugins:

import { MetaPlugin } from "@player-ui/meta-plugin";
const pluginGroup = new MetaPlugin([new Plugin1(), new Plugin2()]);

Add the plugin to Player:

import { Player } from "@player-ui/player";
-
const player = new Player({
plugins: [pluginGroup],
});

You can share pluginGroup with others as an easy way to group multiple plugin features together.

\ No newline at end of file +
const player = new Player({
plugins: [pluginGroup],
});

You can share pluginGroup with others as an easy way to group multiple plugin features together.

\ No newline at end of file diff --git a/next/plugins/core/stage-revert-data/index.html b/next/plugins/core/stage-revert-data/index.html index 54a5c5e6..4183ff8f 100644 --- a/next/plugins/core/stage-revert-data/index.html +++ b/next/plugins/core/stage-revert-data/index.html @@ -1,4 +1,4 @@ - Stage Revert Data | Player - Skip to content
Skip to content

Stage Revert Data

This plugin enables users to temporarily stage data changes before committing to the actual data model

+

Stage Revert Data

This plugin enables users to temporarily stage data changes before committing to the actual data model

A stageData property flag inside of the view properties must be added on the desired view configs.

{
"VIEW_1": {
"state_type": "VIEW",
"ref": "view-1",
"attributes": {
"stageData": true,
"commitTransitions": ["VIEW_2"]
},
"transitions": {
"next": "VIEW_2",
"*": "ACTION_1"
}
}
}

It also should include a list of acceptable commitTransitions valid VIEW name for the data to be committed when the transition occurs, A not included commit transition would trigger the staged data to be cleared. An acceptable transition will commit the data into the data model. e.g. as per the previous example transitioning to VIEW_2 will trigger the staged data to get committed in the model, since the next transition property is pointing to it and is listed on the commitTransitions array parameter, otherwise it would get thrown away.

@@ -102,5 +102,5 @@

Example

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

Simply add the plugin to the config when constructing a player instance.

import StageRevertPlugin from "@player/stage-revert-data";
-
const player = new Player({
plugins: [new StageRevertPlugin()],
});
\ No newline at end of file +

Simply add the plugin to the config when constructing a player instance.

import StageRevertPlugin from "@player/stage-revert-data";
+
const player = new Player({
plugins: [new StageRevertPlugin()],
});
\ No newline at end of file diff --git a/next/plugins/core/types-provider/index.html b/next/plugins/core/types-provider/index.html index cbd90cdc..d6984dd2 100644 --- a/next/plugins/core/types-provider/index.html +++ b/next/plugins/core/types-provider/index.html @@ -1,4 +1,4 @@ - Types Provider | Player - Skip to content
Skip to content

Types Provider

Similar to the Expression Plugin, this plugin adds support for easily exposing new DataTypes, formats, and validations to Player’s content.

+

Types Provider

Similar to the Expression Plugin, this plugin adds support for easily exposing new DataTypes, formats, and validations to Player’s content.

Example

Define a new validation type:

import { ValidatorFunction } from "@player-ui/player";
+

Define a new validation type:

import { ValidatorFunction } from "@player-ui/player";
const customValidator: ValidatorFunction = (context, value) => {
if (value === "bad-value") {
return {
message: "This is a bad value.",
};
}
};

Create a new DataType that references it:

import { Schema } from "@player-ui/player";
const CustomDataType: Schema.DataType = {
name: "CustomType",
validation: [
{
type: "custom-validator",
},
],
};

Register it with Player:

import { Player } from "@player-ui/player";
import { TypesProviderPlugin } from "@player-ui/types-provider-plugin";
-
const player = new Player({
plugins: [
new TypesProviderPlugin({
types: [CustomDataType],
validations: [["custom-validator", customValidator]],
}),
],
});

Given a data-type reference to CustomType in the content, your new validation will be used:

{
"schema": {
"ROOT": {
"data": {
"type": "CustomDataType"
}
}
}
}
\ No newline at end of file +
let formatter = FormatDeclaration(
name: "customFormatter",
format: formatFunction,
deformat: nil
)
let plugin = TypesProviderPlugin(types: [], validators: [], formats: [formatter])

then in the JSON schema for your type:

"schema": {
"ROOT": {
"<yourBinding>": {
"format": {
"type": "customFormatter"
}
}
}
}
Formatting Options

The second parameter passed to the format/deformat functions is for additional options, it is of type [String: Any] and contains any other keys that were passed alongside the type of the formatter:

"format": {
"type": "customFormatter",
"character": "X"
}
let formatFunction = {value, options in
if let stringValue = value as? String {
let char = options["character"] as? String ?? ","
return stringValue.replacingOccurrences(of: ".", with: char)
// Turn all periods into the specified character
} else {
return value
}
}

Custom Types

Just as you can define custom formats and validation, you can define a custom type that encapsulates that functionality into a type, to avoid the need to keep specifying options, this is how the common-types are defined, so when you choose a type like DateType the formatting is already set up.

let type = CustomType(
type: "CustomType",
validation: [
ValidationReference(type: "customValidator")
],
format: FormatReference(type: "customFormatter")
)
+
// Construct the plugin
let plugin = TypesProviderPlugin(types: [type], validators: [validator], formats: [formatter])

then in your JSON schema:

"schema": {
"ROOT": {
"foo": {
"type": "CustomType"
}
}
}
Options in the CustomType

You can supply options to formatters of your custom type in the ValidationReference or FormatReference:

let type = CustomType(
type: "CustomType",
validation: [
ValidationReference(type: "customValidator")
],
format: FormatReference(type: "customFormatter", options: ["character": "X"])
)
\ No newline at end of file diff --git a/next/plugins/index.html b/next/plugins/index.html index 0e2d806d..49584060 100644 --- a/next/plugins/index.html +++ b/next/plugins/index.html @@ -1,4 +1,4 @@ - Plugins | Player - Skip to content
GitHub

Plugins

Plugins are one of the main ways to customize Player to suite individual use-cases. Internally they allow access to many of the core sub-systems, which can add features, configuration, or custom behaviors.

Plugins Overview
-

The scope of what a plugin is capable of is pretty broad, but are typically broken down into smaller reusable modules. Some are more end-user focused (Common Expression Plugin and Common Types Plugin) while others are more relavant for other plugin developers (Expression Plugin and Types Provider Plugin)

\ No newline at end of file +

The scope of what a plugin is capable of is pretty broad, but are typically broken down into smaller reusable modules. Some are more end-user focused (Common Expression Plugin and Common Types Plugin) while others are more relavant for other plugin developers (Expression Plugin and Types Provider Plugin)

\ No newline at end of file diff --git a/next/plugins/ios/external-action-view-modifier/index.html b/next/plugins/ios/external-action-view-modifier/index.html index 594f34bf..56725cf7 100644 --- a/next/plugins/ios/external-action-view-modifier/index.html +++ b/next/plugins/ios/external-action-view-modifier/index.html @@ -1,4 +1,4 @@ - External Action View Modifier | Player - - Skip to content
Skip to content

External Action View Modifier

This plugin is used to handle EXTERNAL states, allowing you to asynchronously tell Player when, and what to transition with once you have finished processing the external state request.

+

External Action View Modifier

This plugin is used to handle EXTERNAL states, allowing you to asynchronously tell Player when, and what to transition with once you have finished processing the external state request.

CocoaPods

Add the subspec to your Podfile

pod 'PlayerUI/ExternalActionViewModifierPlugin'
@@ -84,4 +84,4 @@

ExternalStateViewModifier

struct ExternalAlertModifier: ExternalStateViewModifier {
@ObservedObject var plugin: ExternalStateViewModifierPlugin<Self>
init(plugin: ExternalStateViewModifierPlugin<Self>) {
self.plugin = plugin
}
func body(content: Content) -> some View {
// content is the SwiftUIPlayer root view
// plugin.content is the external state content
-
content.alert(isPresented: $plugin.isExternalState) {
plugin.content
}
}
}
\ No newline at end of file +
content.alert(isPresented: $plugin.isExternalState) {
plugin.content
}
}
}
\ No newline at end of file diff --git a/next/plugins/ios/swiftui-pending-transaction/index.html b/next/plugins/ios/swiftui-pending-transaction/index.html index 42654ee8..78a5f996 100644 --- a/next/plugins/ios/swiftui-pending-transaction/index.html +++ b/next/plugins/ios/swiftui-pending-transaction/index.html @@ -1,4 +1,4 @@ - SwiftUIPendingTransactionPlugin | Player - - Skip to content
Skip to content

SwiftUIPendingTransactionPlugin

The SwiftUIPendingTransactionPlugin allows you to register pending transactions (callbacks) in the userInfo on the decoder. Users can decide when to register, commit and clear transactions based on the use case. Anytime there is a scenario where we want a native transaction to happen while a view update is taking place, we can make use of this plugin. Below is an example used in the sample app where we can see this take place:

+

SwiftUIPendingTransactionPlugin

The SwiftUIPendingTransactionPlugin allows you to register pending transactions (callbacks) in the userInfo on the decoder. Users can decide when to register, commit and clear transactions based on the use case. Anytime there is a scenario where we want a native transaction to happen while a view update is taking place, we can make use of this plugin. Below is an example used in the sample app where we can see this take place:

CocoaPods

Add the subspec to your Podfile

pod 'PlayerUI/SwiftUIPendingTransactionPlugin'
@@ -105,4 +105,4 @@

The Solution:

Calling the new function inside of the action handler:

model.data.run?.commitCallbacksThenCall()

Step 5:

-

Remove the registered transaction with the “input” phase once editing ended (as seen in the code snippet from step 1) because at this point callback has already been committed.

\ No newline at end of file +

Remove the registered transaction with the “input” phase once editing ended (as seen in the code snippet from step 1) because at this point callback has already been committed.

\ No newline at end of file diff --git a/next/plugins/ios/transition/index.html b/next/plugins/ios/transition/index.html index c4eb76d6..f8529bbc 100644 --- a/next/plugins/ios/transition/index.html +++ b/next/plugins/ios/transition/index.html @@ -1,4 +1,4 @@ - Transition Plugin | Player - - Skip to content
Skip to content

Transition Plugin

The TransitionPlugin allows for specifying transitions for when Player loads a flow, and for transition between views in the same flow.

+

Transition Plugin

The TransitionPlugin allows for specifying transitions for when Player loads a flow, and for transition between views in the same flow.

CocoaPods

Add the subspec to your Podfile

pod 'PlayerUI/TransitionPlugin'
@@ -83,4 +83,4 @@

Default Transitions

/// Transition that slides views in from the trailing edge and fades out views on removal
PlayerViewTransition.slideInFadeOut
/// Transition that fades views in and slides out to the leading edge of the screen on removal
PlayerViewTransition.fadeInSlideOut
/// Transition that slides views in from the trailing edge and out from to the leading edge of the screen
PlayerViewTransition.push
-
/// Transition that slides views in from the leading edge and out from to the trailing edge of the screen
PlayerViewTransition.pop
\ No newline at end of file +
/// Transition that slides views in from the leading edge and out from to the trailing edge of the screen
PlayerViewTransition.pop
\ No newline at end of file diff --git a/next/plugins/multiplatform/asset-provider/index.html b/next/plugins/multiplatform/asset-provider/index.html index 45f99368..309c0c02 100644 --- a/next/plugins/multiplatform/asset-provider/index.html +++ b/next/plugins/multiplatform/asset-provider/index.html @@ -1,4 +1,4 @@ - Asset Provider Plugin | Player - Skip to content
Skip to content

Asset Provider Plugin

The Asset Provider Plugin enables users to easily register UI components to render their assets. It’s used internally by the Reference Assets. The matches follow the same rules as asset transforms (more specific matches take priority).

+

Asset Provider Plugin

The Asset Provider Plugin enables users to easily register UI components to render their assets. It’s used internally by the Reference Assets. The matches follow the same rules as asset transforms (more specific matches take priority).

Usage

Platform

-

Install the plugin:

Terminal window
yarn add @player-ui/asset-provider-plugin-react

Create an instance, and add it to your Player instance. -The API is similar to the JavaScript Map, and takes a list of [match, Component] tuples.

import { ReactPlayer } from "@player-ui/react";
import { AssetProviderPlugin } from "@player-ui/asset-provider-plugin-react";
-
const player = new ReactPlayer({
plugins: [
new AssetProviderPlugin([
["custom-asset", () => <div>Hello World!</div>],
[{ type: "custom", key: "asset" }, () => <div>Other Custom Asset</div>],
]),
],
});

This will register a match on { type: 'custom-asset' } and { type: 'custom', key: 'asset' } in the view to use your React components.

+

Install the plugin:

Terminal window
npm i @player-ui/asset-provider-plugin-react

Create an instance, and add it to your Player instance. +The API is similar to the JavaScript Map, and takes a list of [match, Component] tuples.

import { ReactPlayer } from "@player-ui/react";
import { AssetProviderPlugin } from "@player-ui/asset-provider-plugin-react";
+
const player = new ReactPlayer({
plugins: [
new AssetProviderPlugin([
["custom-asset", () => <div>Hello World!</div>],
[{ type: "custom", key: "asset" }, () => <div>Other Custom Asset</div>],
]),
],
});

This will register a match on { type: 'custom-asset' } and { type: 'custom', key: 'asset' } in the view to use your React components.

Content

In this example, when your content has assets of type custom-asset and custom, they will render <div>Hello World!</div> and <div>Other Custom Asset</div>.

import { Custom, CustomAsset, Collection } from "my-assets";
-
const view = (
<Collection>
<CustomAsset />
<Custom />
</Collection>
);
\ No newline at end of file +
import { Custom, CustomAsset, Collection } from "my-assets";
+
const view = (
<Collection>
<CustomAsset />
<Custom />
</Collection>
);
\ No newline at end of file diff --git a/next/plugins/multiplatform/async-node/index.html b/next/plugins/multiplatform/async-node/index.html index 47faf23d..6d3de8db 100644 --- a/next/plugins/multiplatform/async-node/index.html +++ b/next/plugins/multiplatform/async-node/index.html @@ -1,4 +1,4 @@ - AsyncNode Plugin | Player - Skip to content
Skip to content

AsyncNode Plugin

The AsyncNode Plugin is used to enable streaming additional content into a flow that has already been loaded and rendered.
+

AsyncNode Plugin

The AsyncNode Plugin is used to enable streaming additional content into a flow that has already been loaded and rendered.
A common use case for this plugin is conversational UI, as the users input more dialogue, new content must be streamed into Player in order to keep the UI up to date.

The pillar that makes this possible is the concept of an AsyncNode. An AsyncNode is any tree node with the property async: true, it represents a placeholder node that will be replaced by a concrete node in the future.

In the example below, node with the id “some-async-node” will not be rendered on first render, but will be replaced with a UI asset node at a later time:

@@ -111,20 +111,20 @@

Usage

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

Add the plugin to Player:

import { Player } from '@player-ui/player';
import { AsyncNodePlugin, AsyncNodePluginPlugin } from '@player-ui/async-node-plugin';
+

Add the plugin to Player:

import { Player } from '@player-ui/player';
import { AsyncNodePlugin, AsyncNodePluginPlugin } from '@player-ui/async-node-plugin';
const asyncNodePlugin = new AsyncNodePlugin({
plugins: [new AsyncNodePluginPlugin()],
});
// Configuring async node behaviour
asyncNodePlugin.hooks.onAsyncNode.tap('handleAsync', async (node: Node.Node) => {
...
// Determine what to return to be parsed into a concrete UI asset
});
// For use cases where the async node needs to be updated multiple times
asyncNodePlugin.hooks.onAsyncNode.tap("toast-provider", async (node: Node.Async, update: (content) => void) => {
...
// do some async task to get content
const toastContent = await makeToastFor(node.id);
// set timer for 5 seconds to remove the toast content from the view
setTimeout(() => update(null), 5000);
// uses same mechanism as before
return toastContent;
});
-
const player = new Player({
plugins: [
asyncNodePlugin
]
})
\ No newline at end of file +
return .multiNode([
ReplacementNode.encodable(PlaceholderNode(id: "text", type: "text", value: "1st value in the multinode")),
ReplacementNode.encodable(AsyncNode(id: "id"))])

Note: the AsyncNode struct is already defined in the plugin with the async property defaulted to true so only id needs to be passed in

As a convenience to the user, the AsyncNodePlugin just takes a callback which has the content to be returned, this is provided to the plugin which calls the the onAsyncNode hook tap method. The return could be a single asset node or an array of asset nodes, or null if the async node is no longer relevant.

\ No newline at end of file diff --git a/next/plugins/multiplatform/beacon/index.html b/next/plugins/multiplatform/beacon/index.html index ae3b6102..9759c56b 100644 --- a/next/plugins/multiplatform/beacon/index.html +++ b/next/plugins/multiplatform/beacon/index.html @@ -1,4 +1,4 @@ - Beacon Plugin | Player - Skip to content
Skip to content

Beacon Plugin

The beacon plugin enables users to send and/or collect beaconing information from assets in a normalized API. It exposes a common API for publishing beacons from an asset library, and will automatically attach itself to the current view, enabling additional meta-data to be added to each event.

+

Beacon Plugin

The beacon plugin enables users to send and/or collect beaconing information from assets in a normalized API. It exposes a common API for publishing beacons from an asset library, and will automatically attach itself to the current view, enabling additional meta-data to be added to each event.

Consuming Beacons

Beacon Format

By default, the beacon plugin returns beacons in the following format:

@@ -79,14 +79,14 @@

Beacon Format

/** The id of the asset **/
assetId: string;
/** The id of the view **/
viewId: string;
}

Usage

-

Add the beacon plugin to a player:

import { Player } from "@player-ui/player";
import { BeaconPlugin } from "@player-ui/beacon-plugin";
+

Add the beacon plugin to a player:

import { Player } from "@player-ui/player";
import { BeaconPlugin } from "@player-ui/beacon-plugin";
const player = new Player({
plugins: [
new BeaconPlugin({
// Any plugins to the beacon-plugin
plugins: [],
-
// Callback to handle any beacon event
callback: () => {},
}),
],
});

Beacons can be published directly by the plugin, but in most cases, a platform specific adapter is recommended.

beaconPlugin.beacon({
action: "click",
element: "button",
asset: asset, // The entire Asset object, for use in the plugin pipeline
// other metadata
});
+
// In some SwiftUI View
var body: some View {
SwiftUIPlayer(
flow: flow,
plugins: [
BeaconPlugin<CustomBeacon> { (beacon: CustomBeacon) in
// Process beacon into the format you need for Segment/Trinity and send it on
}
]
)
}

Beacon Plugins

Similar to how Player accepts plugins, the beacon-plugin itself accepts a list of plugins. These are able to mutate and augment the beacon payload as it makes its way through the publishing pipeline.

There are 3 hooks that are currently exposed:

@@ -128,9 +128,9 @@

Assets

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

useBeacon hook

The @player-ui/beacon-plugin-react package exports a hook that assets can leverage to publish beacons.

The useBeacon hook takes base options that apply broadly, and returns a function with those options as the base. You can then pass event specific information when calling beacon.

Example

import { useBeacon } from "@player-ui/beacon-plugin-react";
+

useBeacon hook

The @player-ui/beacon-plugin-react package exports a hook that assets can leverage to publish beacons.

The useBeacon hook takes base options that apply broadly, and returns a function with those options as the base. You can then pass event specific information when calling beacon.

Example

import { useBeacon } from "@player-ui/beacon-plugin-react";
// inside of your asset
export const Component = (props) => {
const beacon = useBeacon({ action: "clicked", asset: props });
-
return (
<button
onClick={() =>
beacon({
element: "button",
data: {
custom: "fields",
},
})
}
>
Click Me
</button>
);
};
\ No newline at end of file +
override fun View.hydrate() {
val binding = MyCustomViewBinding.bind(this)
binding.button.setOnClickListener {
this@MyAsset.beacon(
action = BeaconAction.clicked,
element = BeaconElement.button,
asset = this@MyAsset,
data = null
)
}
...
}
}
\ No newline at end of file diff --git a/next/plugins/multiplatform/check-path/index.html b/next/plugins/multiplatform/check-path/index.html index 9e8458ea..224afeed 100644 --- a/next/plugins/multiplatform/check-path/index.html +++ b/next/plugins/multiplatform/check-path/index.html @@ -1,4 +1,4 @@ - Check Path Plugin | Player - Skip to content
Skip to content

Check Path Plugin

The Check Path Plugin enables users to query segments of the view tree for contextual rendering or behavior. +

Check Path Plugin

The Check Path Plugin enables users to query segments of the view tree for contextual rendering or behavior. This is best suited to be referenced during the UI rendering phase, where one can make decisions about the rendering of an asset based on where it lies in the tree.

Usage

+

Install the plugin:

Terminal window
npm i @player-ui/check-path-plugin

Add it to Player:

import { CheckPathPlugin } from '@player-ui/check-path-plugin';
+
const checkPathPlugin = new CheckPathPlugin();
const player = new Player({ plugins: [checkPathPlugin] });
+
// Start your flow
player.start(myFlow);

Then use the plugin to query the view:

const isCustomThing = checkPathPlugin.hasParentContext("my-asset-id", [
"input",
"myCustomViewType",
]);
+

API

Install the plugin:

Terminal window
yarn add @player-ui/check-path-plugin

Add it to Player:

import CheckPathPlugin from "@player-ui/check-path-plugin";
-
const checkPathPlugin = new CheckPathPlugin();
const player = new Player({ plugins: [checkPathPlugin] });
-
// Start your flow
player.start(myFlow);

Then use the plugin to query the view:

const isCustomThing = checkPathPlugin.hasParentContext("my-asset-id", [
"input",
"myCustomViewType",
]);
-

API

-

Query

In most of the methods, the Query type is referenced. +

Query

In most of the methods, the Query type is referenced. This can either be a function, string, or an object.

  • string - an alias for { type: '<string>' }
  • object - uses a partial-object match to query the object
  • function - a filter that gets passed an object, and returns a boolean

There are a few different functions exposed by the plugin you can use:

getParent

function getParent(id: string, query: Query | Query[]): Asset | undefined;

The getParent method allows you to query up the tree, and return the first parent that matches the given query, or undefined. If an array is passed, the tree is parsed matching on each query item from left to right. The parent object that matches the last query item is returned.


getParentProp

function getParentProp(id: string): string | undefined;

The getParentProp method returns the property on the parent object that the current object falls under. -For example, an input with a text asset as the label, will return label for the parentProp of the text asset.


hasParentContext

function hasParentContext(id: string, query: Query | Query[]): boolean;

Similar to getParent, the hasParentContext method responds with the existence of a parent with the given context.


hasChildContext

function hasChildContext(id: string, query: Query | Query[]): boolean;

The compliment of hasParentContext, hasChildContext traverses down the tree for any path from the given node that satisfies the context requirements.

\ No newline at end of file +For example, an input with a text asset as the label, will return label for the parentProp of the text asset.


hasParentContext

function hasParentContext(id: string, query: Query | Query[]): boolean;

Similar to getParent, the hasParentContext method responds with the existence of a parent with the given context.


hasChildContext

function hasChildContext(id: string, query: Query | Query[]): boolean;

The compliment of hasParentContext, hasChildContext traverses down the tree for any path from the given node that satisfies the context requirements.

\ No newline at end of file diff --git a/next/plugins/multiplatform/console-logger/index.html b/next/plugins/multiplatform/console-logger/index.html index 7f6bc0ee..154dca09 100644 --- a/next/plugins/multiplatform/console-logger/index.html +++ b/next/plugins/multiplatform/console-logger/index.html @@ -1,4 +1,4 @@ - Console Logger | Player - Skip to content
Skip to content

Console Logger

A plugin to easily enable logs to be written to the JS console. Extremely helpful for local Player development and debugging.

+

Console Logger

A plugin to easily enable logs to be written to the JS console. Extremely helpful for local Player development and debugging.

Usage

Install the plugin:

import { Player } from "@player-ui/player";
import { ConsoleLoggerPlugin } from "@player-ui/console-logger-plugin";
+

Install the plugin:

import { Player } from "@player-ui/player";
import { ConsoleLoggerPlugin } from "@player-ui/console-logger-plugin";
const consoleLogger = new ConsoleLoggerPlugin();
-
const player = new Player({
plugins: [consoleLogger],
});

To change the severity:

consoleLogger.setSeverity("warn");
\ No newline at end of file +
const player = new Player({
plugins: [consoleLogger],
});

To change the severity:

consoleLogger.setSeverity("warn");
\ No newline at end of file diff --git a/next/plugins/multiplatform/external-action/index.html b/next/plugins/multiplatform/external-action/index.html index 9ff7dac0..0314376b 100644 --- a/next/plugins/multiplatform/external-action/index.html +++ b/next/plugins/multiplatform/external-action/index.html @@ -1,4 +1,4 @@ - External Action | Player - Skip to content
Skip to content

External Action

The External Action Plugin is an easy way to handle External states from the navigation of a Player flow.

+

External Action

The External Action Plugin is an easy way to handle External states from the navigation of a Player flow.

Usage

-

Install the plugin:

Terminal window
yarn add @player-ui/external-action-plugin

Create a handler for external actions:

import { ExternalStateHandler } from '@player-ui/external-action-plugin';
+
Terminal window
npm i @player-ui/external-action-plugin

Create a handler for external actions:

import { ExternalStateHandler } from '@player-ui/external-action-plugin';
const externalActionHandler: ExternalStateHandler = async (state, options) => {
if (state.ref === 'custom') {
return 'next';
}
}

Add it to Player:

import { Player } from "@player-ui/player";
import { ExternalActionPlugin } from "@player-ui/external-action-plugin";
-
const player = new Player({
plugins: [new ExternalActionPlugin(externalActionHandler)],
});

This will transition any EXTERNAL state in Player’s navigation, with a ref property of custom using the next transition.

const player = new Player({
plugins: [new ExternalActionPlugin(externalActionHandler)],
});

This will transition any EXTERNAL state in Player’s navigation, with a ref property of custom using the next transition.

val externalActionPlugin = ExternalActionPlugin { state, options, transition ->
// access external state node
val extraProperty = state["extraProperty"]
// access data model
options.data.get("{{foo.bar}}")
// evaluate expression
options.expression.evaluate("{{foo.bar}} = 1")
// do other processing or show non-player experience
// transition to the next node using "Next"
transition("Next")
}
AndroidPlayer(externalActionPlugin)
// handler can be configured after instantiation
externalActionPlugin.onExternalAction { /** handle external action */ }
-
// extension method for configuring plugin with player instance
androidPlayer.onExernalAction { /** handle external action */ }

In Player content

{
"navigation": {
"BEGIN": "FLOW_1",
"FLOW_1": {
"startState": "EXT_1",
"EXT_1": {
"state_type": "EXTERNAL",
"ref": "test-1",
"transitions": {
"Next": "END_FWD",
"Prev": "END_BCK"
},
"extraProperty": "extraValue"
},
"END_FWD": {
"state_type": "END",
"outcome": "FWD"
},
"END_BCK": {
"state_type": "END",
"outcome": "BCK"
}
}
}
}

API

Public API available from sources here:

ExternalActionPlugin

\ No newline at end of file +
var body: some View {
SwiftUIPlayer(
flow: flow,
plugins: [
plugin
],
result: $resultBinding
)
}
\ No newline at end of file diff --git a/next/plugins/multiplatform/metrics/index.html b/next/plugins/multiplatform/metrics/index.html index ff8c7c6a..2ae26393 100644 --- a/next/plugins/multiplatform/metrics/index.html +++ b/next/plugins/multiplatform/metrics/index.html @@ -1,4 +1,4 @@ - Metrics | Player - Skip to content
Skip to content

Metrics

The Metrics Plugin is used to gather timing information about Player’s execution of a flow. There are also platform specific integrations to include render and update times.

+

Metrics

The Metrics Plugin is used to gather timing information about Player’s execution of a flow. There are also platform specific integrations to include render and update times.

The diagram below illistrates some of the timing information gathered:

Metrics Timing Diagram

Usage

@@ -102,12 +102,12 @@

Usage

} customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { MetricsPlugin } from "@player-ui/metrics-plugin";
+

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { MetricsPlugin } from "@player-ui/metrics-plugin";
const player = new Player({
plugins: [
new MetricsPlugin({
onUpdate: (metrics) => {
// Handle the update
},
}),
],
});

The onUpdate callback will be invoked for any update to the metrics. There are also callbacks for finer-grained events (onRenderEnd, onInteractive, etc), as well as a hooks based API for even more control.

Using a custom timer

By default, all time is measured in ms using performance.now() with a fallback to the less-accurate Date.now(). -If you wish to supply your own timer, simply use the getTime option to set the function to use.

Measuring Render Time

For extensions of this plugin that wish to track the render (and update) times of nodes, add the trackRenderTime flag to options. You must then call metrics.renderEnd() to denote when content is painted on the screen. This is automatically handled for the platform specific versions of this plugin.

+If you wish to supply your own timer, simply use the getTime option to set the function to use.

Measuring Render Time

For extensions of this plugin that wish to track the render (and update) times of nodes, add the trackRenderTime flag to options. You must then call metrics.renderEnd() to denote when content is painted on the screen. This is automatically handled for the platform specific versions of this plugin.

Beaconing

-

The Metrics Plugin also includes a plugin for the Beacon Plugin that adds render time to the hook context for viewed beacons send for views. This plugin is automatically registered to the Beacon Plugin if the trackRenderTime option is enabled.

In order to actually include the render-time in a beacon, you must create a BeaconPluginPlugin that maps the renderTime from the hook’s context to the actual beacon object. It can be accessed through the MetricsViewBeaconPluginContextSymbol key:

import { MetricsViewBeaconPluginContextSymbol } from '@player-ui/metrics-plugin';
import { BeaconPluginPlugin } from '@player-ui/beacon-plugin';
+

The Metrics Plugin also includes a plugin for the Beacon Plugin that adds render time to the hook context for viewed beacons send for views. This plugin is automatically registered to the Beacon Plugin if the trackRenderTime option is enabled.

In order to actually include the render-time in a beacon, you must create a BeaconPluginPlugin that maps the renderTime from the hook’s context to the actual beacon object. It can be accessed through the MetricsViewBeaconPluginContextSymbol key:

import { MetricsViewBeaconPluginContextSymbol } from '@player-ui/metrics-plugin';
import { BeaconPluginPlugin } from '@player-ui/beacon-plugin';
class MyBeaconPluginPlugin implements BeaconPluginPlugin {
apply(beaconPlugin: BeaconPlugin) {
beaconPlugin.hooks.buildBeacon.tap(
{ name: 'my-beacon-plugin', context: true } as Tap,
async (context, beacon) => {
const { renderTime } =
(await (context as any)[MetricsViewBeaconPluginContextSymbol]) || {};
-
return {
...beacon,
...(renderTime && { renderTime }),
};
}
);
}

See the Beacon Plugin for more info.

\ No newline at end of file +
return {
...beacon,
...(renderTime && { renderTime }),
};
}
);
}

See the Beacon Plugin for more info.

\ No newline at end of file diff --git a/next/plugins/multiplatform/partial-match-fingerprint/index.html b/next/plugins/multiplatform/partial-match-fingerprint/index.html index 1fbf1ab5..e279e7cc 100644 --- a/next/plugins/multiplatform/partial-match-fingerprint/index.html +++ b/next/plugins/multiplatform/partial-match-fingerprint/index.html @@ -1,4 +1,4 @@ - Partial Match | Player - Skip to content
Skip to content

Partial Match

This plugin enables users to map matches of assets to any arbitrary value in a partial-match-registry. +

Partial Match

This plugin enables users to map matches of assets to any arbitrary value in a partial-match-registry. For each asset in a resolved view, the matches will be computed.

Usage

Create a registry and add matches:

import { Registry } from "@player-ui/partial-match-registry";
+

Create a registry and add matches:

import { Registry } from "@player-ui/partial-match-registry";
const registry = new Registry([[{ type: "action" }, "ABC"]]);

Add the registy to a plugin:

import { Player } from "@player-ui/player";
import { PartialMatchFingerprintPlugin } from "@player-ui/partial-match-fingerprint-plugin";
const matchPlugin = new PartialMatchFingerprintPlugin(registry);
-
const player = new Player({
plugins: [matchPlugin],
});

Query the plugin for matches:

const value = matchPlugin.get("asset-id"); // 'ABC'
\ No newline at end of file +
// get the index for the asset with matching ID if it was resolved in the core plugin
plugin.get(assetId: "test")
\ No newline at end of file diff --git a/next/plugins/multiplatform/pub-sub/index.html b/next/plugins/multiplatform/pub-sub/index.html index 133710ef..09579738 100644 --- a/next/plugins/multiplatform/pub-sub/index.html +++ b/next/plugins/multiplatform/pub-sub/index.html @@ -1,4 +1,4 @@ - PubSub | Player - Skip to content
Skip to content

PubSub

The PubSub plugin adds a publish/subscribe interface between the host app and Player’s content.

+

PubSub

The PubSub plugin adds a publish/subscribe interface between the host app and Player’s content.

Usage

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { PubSubPlugin } from "@player-ui/pub-sub-plugin";
+

Add the plugin to Player:

import { Player } from "@player-ui/player";
import { PubSubPlugin } from "@player-ui/pub-sub-plugin";
const pubsub = new PubSubPlugin();
const token = pubsub.subscribe("some-event", () => {
// Callback
});
-
const player = new Player({
plugins: [pubsub],
});

To unsubscribe:

pubsub.unsubscribe(token);
+
// extension method for removing a specific event subscription
player.unsubscribe(token)

Publish Expression

To trigger an event to be published, use the publish() expression in Player’s content:

-
{
"asset": {
"id": "sample",
"type": "action",
"exp": "@[ publish('some-event', 'some optional data') ]@""
}
}
\ No newline at end of file +
{
"asset": {
"id": "sample",
"type": "action",
"exp": "@[ publish('some-event', 'some optional data') ]@""
}
}
\ No newline at end of file diff --git a/next/plugins/multiplatform/shared-constants/index.html b/next/plugins/multiplatform/shared-constants/index.html index c52c973a..6e7520db 100644 --- a/next/plugins/multiplatform/shared-constants/index.html +++ b/next/plugins/multiplatform/shared-constants/index.html @@ -1,4 +1,4 @@ - Shared Constants | Player - Skip to content
Skip to content

Shared Constants

The Shared Constants Plugin enables users to define and override commonly used static values. It can be leveraged by other plugins to enable localization.

+

Shared Constants

The Shared Constants Plugin enables users to define and override commonly used static values. It can be leveraged by other plugins to enable localization.

Usage

Create the plugin and add it to Player:

import { Player } from "@player-ui/player";
import { ConstantsPlugin } from "@player-ui/shared-constants-plugin";
+

Create the plugin and add it to Player:

import { Player } from "@player-ui/player";
import { ConstantsPlugin } from "@player-ui/shared-constants-plugin";
const constantsPlugin = new ConstantsPlugin({
data: {
prop1: "A",
prop2: "B",
},
namespace: "constants",
dataPath: "data.constants",
});
-
const player = new Player({
plugins: [constantsPlugin],
});

You can then query the plugin to get the value of a particular key:

constantsPlugin.getConstants("prop1"); // 'A'
+
const player = new Player({
plugins: [constantsPlugin],
});

You can then query the plugin to get the value of a particular key:

constantsPlugin.getConstants("prop1"); // 'A'

Overriding Values in Content

The dataPath configuration option enables content to override specific values for a particular flow:

{
"data": {
"constants": {
"prop1": "B"
}
}
}
@@ -114,4 +114,4 @@

Common Types Plugin

The Common Types Plugin leverages this pattern to allow for global contextual message overrides. In order to override those messages you may use something like:

import { Player } from "@player-ui/player";
import { ConstantsPlugin } from "@player-ui/constants-plugion";
import { CommonTypesPlugin } from "@player-ui/common-types-plugin";
const player = new Player({
plugins: [
new CommonTypesPlugin(),
new ConstantsPlugin({
namespace: "constants",
dataPath: "data.constants",
data: {
validation: {
lengthError: {
minimum: "Way too short",
maximum: "Way too long",
},
},
},
}),
],
});
-

Any triggerd validation for the length validation will now use the custom error messages. See the Common Types Plugin for more information on the supported overrides and paths.

\ No newline at end of file +

Any triggerd validation for the length validation will now use the custom error messages. See the Common Types Plugin for more information on the supported overrides and paths.

\ No newline at end of file diff --git a/next/plugins/react/auto-scroll-manager/index.html b/next/plugins/react/auto-scroll-manager/index.html index 96ea7242..519b1dfa 100644 --- a/next/plugins/react/auto-scroll-manager/index.html +++ b/next/plugins/react/auto-scroll-manager/index.html @@ -1,4 +1,4 @@ - Auto Scroll Manager Plugin | Player - Skip to content
Skip to content

Auto Scroll Manager Plugin

The Auto Scroll Plugin helps orchestrate scrolling to components based off of 3 scenarios:

+

Auto Scroll Manager Plugin

The Auto Scroll Plugin helps orchestrate scrolling to components based off of 3 scenarios:

  • On page load
  • On validation trigger
  • @@ -104,8 +104,8 @@

    Usage

    } customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() -

    Pull in the plugin and attach it to a player instance:

    import { ReactPlayer } from "@player-ui/react";
    import { AutoScrollManagerPlugin } from "@player-ui/auto-scroll-manager-plugin-react";
    +

    Pull in the plugin and attach it to a player instance:

    import { ReactPlayer } from "@player-ui/react";
    import { AutoScrollManagerPlugin } from "@player-ui/auto-scroll-manager-plugin-react";
    const reactPlayer = new ReactPlayer({
    plugins: [new AutoScrollManagerPlugin()],
    });

    Next you’ll use the provided useRegisterAsScrollable hook to register a component as a scrollable target in a useLayoutEffect or similar hook.

    import {
    useRegisterAsScrollable,
    ScrollType,
    } from "@player-ui/auto-scroll-manager-plugin-react";
    const CustomScrollableAsset = (props: CustomAssetType) => {
    const registerFunction = useRegisterAsScrollable();
    useLayoutEffect(() => {
    registerFunction({
    type: ScrollType.FirstAppearance,
    ref: props.id,
    });
    }, []);
    -
    return <SomeComponent />;
    };
\ No newline at end of file +
return <SomeComponent />;
};
\ No newline at end of file diff --git a/next/storybook-demo/ReactPlayer-mdx.657a80aa.iframe.bundle.js b/next/storybook-demo/ReactPlayer-mdx.c828b3f1.iframe.bundle.js similarity index 99% rename from next/storybook-demo/ReactPlayer-mdx.657a80aa.iframe.bundle.js rename to next/storybook-demo/ReactPlayer-mdx.c828b3f1.iframe.bundle.js index dd1b28e0..be16b6ca 100644 --- a/next/storybook-demo/ReactPlayer-mdx.657a80aa.iframe.bundle.js +++ b/next/storybook-demo/ReactPlayer-mdx.c828b3f1.iframe.bundle.js @@ -1,2 +1,2 @@ -/*! For license information please see ReactPlayer-mdx.657a80aa.iframe.bundle.js.LICENSE.txt */ -"use strict";(self.webpackChunkstorybook_docs=self.webpackChunkstorybook_docs||[]).push([[157,345,944],{"../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{BN:()=>MDXContext,RP:()=>useMDXComponents,gz:()=>withMDXComponents,xA:()=>MDXProvider});var react__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");const MDXContext=react__WEBPACK_IMPORTED_MODULE_0__.createContext({});function withMDXComponents(Component){return function boundMDXComponent(props){const allComponents=useMDXComponents(props.components);return react__WEBPACK_IMPORTED_MODULE_0__.createElement(Component,{...props,allComponents})}}function useMDXComponents(components){const contextComponents=react__WEBPACK_IMPORTED_MODULE_0__.useContext(MDXContext);return react__WEBPACK_IMPORTED_MODULE_0__.useMemo((()=>"function"==typeof components?components(contextComponents):{...contextComponents,...components}),[contextComponents,components])}const emptyObject={};function MDXProvider({components,children,disableParentContext}){let allComponents;return allComponents=disableParentContext?"function"==typeof components?components({}):components||emptyObject:useMDXComponents(components),react__WEBPACK_IMPORTED_MODULE_0__.createElement(MDXContext.Provider,{value:allComponents},children)}},"../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{W8:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.W8,gG:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.gG});__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/chunk-HLWAVYOI.mjs");var _storybook_blocks__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+blocks@7.6.19_830451985/node_modules/@storybook/blocks/dist/index.mjs")},"./src/ManagedPlayer.stories.tsx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{AssetErrorHandling:()=>AssetErrorHandling,ContentErrorHandling:()=>ContentErrorHandling,SimpleFlow:()=>SimpleFlow,__namedExportsOrder:()=>__namedExportsOrder,default:()=>ManagedPlayer_stories});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var dist=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+storybook@0.0.0/node_modules/@player-ui/storybook/dist/index.mjs"),_player_ui_react_dist=(__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs"),__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+react@0.0.0/node_modules/@player-ui/react/dist/index.mjs")),reference_assets_plugin_react_dist=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+reference-assets-plugin-react@0.0.0/node_modules/@player-ui/reference-assets-plugin-react/dist/index.mjs");const first_flow_namespaceObject=JSON.parse('{"id":"flow_1","views":[{"id":"first_view","type":"action","value":"end","label":{"asset":{"id":"next-view-label","type":"text","value":"End Flow 1"}}}],"navigation":{"BEGIN":"flow_1","flow_1":{"startState":"view_1","view_1":{"state_type":"VIEW","ref":"first_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}');function _typeof(o){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},_typeof(o)}function _regeneratorRuntime(){_regeneratorRuntime=function _regeneratorRuntime(){return e};var t,e={},r=Object.prototype,n=r.hasOwnProperty,o=Object.defineProperty||function(t,e,r){t[e]=r.value},i="function"==typeof Symbol?Symbol:{},a=i.iterator||"@@iterator",c=i.asyncIterator||"@@asyncIterator",u=i.toStringTag||"@@toStringTag";function define(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{define({},"")}catch(t){define=function define(t,e,r){return t[e]=r}}function wrap(t,e,r,n){var i=e&&e.prototype instanceof Generator?e:Generator,a=Object.create(i.prototype),c=new Context(n||[]);return o(a,"_invoke",{value:makeInvokeMethod(t,r,c)}),a}function tryCatch(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}e.wrap=wrap;var h="suspendedStart",l="suspendedYield",f="executing",s="completed",y={};function Generator(){}function GeneratorFunction(){}function GeneratorFunctionPrototype(){}var p={};define(p,a,(function(){return this}));var d=Object.getPrototypeOf,v=d&&d(d(values([])));v&&v!==r&&n.call(v,a)&&(p=v);var g=GeneratorFunctionPrototype.prototype=Generator.prototype=Object.create(p);function defineIteratorMethods(t){["next","throw","return"].forEach((function(e){define(t,e,(function(t){return this._invoke(e,t)}))}))}function AsyncIterator(t,e){function invoke(r,o,i,a){var c=tryCatch(t[r],t,o);if("throw"!==c.type){var u=c.arg,h=u.value;return h&&"object"==_typeof(h)&&n.call(h,"__await")?e.resolve(h.__await).then((function(t){invoke("next",t,i,a)}),(function(t){invoke("throw",t,i,a)})):e.resolve(h).then((function(t){u.value=t,i(u)}),(function(t){return invoke("throw",t,i,a)}))}a(c.arg)}var r;o(this,"_invoke",{value:function value(t,n){function callInvokeWithMethodAndArg(){return new e((function(e,r){invoke(t,n,e,r)}))}return r=r?r.then(callInvokeWithMethodAndArg,callInvokeWithMethodAndArg):callInvokeWithMethodAndArg()}})}function makeInvokeMethod(e,r,n){var o=h;return function(i,a){if(o===f)throw Error("Generator is already running");if(o===s){if("throw"===i)throw a;return{value:t,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=maybeInvokeDelegate(c,n);if(u){if(u===y)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===h)throw o=s,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=f;var p=tryCatch(e,r,n);if("normal"===p.type){if(o=n.done?s:l,p.arg===y)continue;return{value:p.arg,done:n.done}}"throw"===p.type&&(o=s,n.method="throw",n.arg=p.arg)}}}function maybeInvokeDelegate(e,r){var n=r.method,o=e.iterator[n];if(o===t)return r.delegate=null,"throw"===n&&e.iterator.return&&(r.method="return",r.arg=t,maybeInvokeDelegate(e,r),"throw"===r.method)||"return"!==n&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+n+"' method")),y;var i=tryCatch(o,e.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,y;var a=i.arg;return a?a.done?(r[e.resultName]=a.value,r.next=e.nextLoc,"return"!==r.method&&(r.method="next",r.arg=t),r.delegate=null,y):a:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,y)}function pushTryEntry(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function resetTryEntry(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function Context(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(pushTryEntry,this),this.reset(!0)}function values(e){if(e||""===e){var r=e[a];if(r)return r.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,i=function next(){for(;++o=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return handle("end");if(i.tryLoc<=this.prev){var c=n.call(i,"catchLoc"),u=n.call(i,"finallyLoc");if(c&&u){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),resetTryEntry(r),y}},catch:function _catch(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;resetTryEntry(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function delegateYield(e,r,n){return this.delegate={iterator:values(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),y}},e}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}var SIMPLE_FLOWS=[first_flow_namespaceObject,JSON.parse('{"id":"flow_2","views":[{"id":"second_view","type":"action","value":"flow_2","label":{"asset":{"id":"next-view-label","type":"text","value":"End View 2"}}}],"navigation":{"BEGIN":"flow_2","flow_2":{"startState":"view_2","view_2":{"state_type":"VIEW","ref":"second_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')],ERROR_CONTENT_FLOW=[first_flow_namespaceObject,JSON.parse('{"id":"flow_2","views":[{"id":"second_view","type":"action","exp":"{{foo.bar..}","value":"end","label":{"asset":{"id":"next-view-label","type":"text","value":"End View 2"}}}],"navigation":{"BEGIN":"flow_2","flow_2":{"startState":"view_2","view_2":{"state_type":"VIEW","ref":"second_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')],ERROR_ASSET_FLOW=[first_flow_namespaceObject,JSON.parse('{"id":"flow_3","views":[{"id":"third_view","type":"error"}],"navigation":{"BEGIN":"flow_3","flow_3":{"startState":"view_3","view_3":{"state_type":"VIEW","ref":"third_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')];function createFlowManager(flowSequence){var dummyManager={next:function next(prevState){return function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}(_regeneratorRuntime().mark((function _callee(){var flowIndex;return _regeneratorRuntime().wrap((function _callee$(_context){for(;;)switch(_context.prev=_context.next){case 0:if(prevState){_context.next=2;break}return _context.abrupt("return",{value:flowSequence[0]});case 2:if(!((flowIndex=flowSequence.indexOf(prevState.flow))>=flowSequence.length-1)){_context.next=5;break}return _context.abrupt("return",{done:!0});case 5:return _context.abrupt("return",{value:flowSequence[flowIndex+1]});case 6:case"end":return _context.stop()}}),_callee)})))()}};return dummyManager}var jsx_runtime=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js");const ManagedPlayer_stories={title:"React Player/Managed Player"};var SimpleFlow={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(SIMPLE_FLOWS)})})}},ContentErrorHandling={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(ERROR_CONTENT_FLOW)})})}},AssetErrorHandling={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(ERROR_ASSET_FLOW)})})}};SimpleFlow.parameters={...SimpleFlow.parameters,docs:{...SimpleFlow.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...SimpleFlow.parameters?.docs?.source}}},ContentErrorHandling.parameters={...ContentErrorHandling.parameters,docs:{...ContentErrorHandling.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...ContentErrorHandling.parameters?.docs?.source}}},AssetErrorHandling.parameters={...AssetErrorHandling.parameters,docs:{...AssetErrorHandling.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...AssetErrorHandling.parameters?.docs?.source}}};const __namedExportsOrder=["SimpleFlow","ContentErrorHandling","AssetErrorHandling"]},"./src/Player.stories.tsx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{ReactPlayer:()=>ReactPlayer,__namedExportsOrder:()=>__namedExportsOrder,default:()=>__WEBPACK_DEFAULT_EXPORT__});var _player_ui_storybook__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+storybook@0.0.0/node_modules/@player-ui/storybook/dist/index.mjs");__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs");const __WEBPACK_DEFAULT_EXPORT__={title:"React Player"};var ReactPlayer=(0,_player_ui_storybook__WEBPACK_IMPORTED_MODULE_1__.D1)((function(){return __webpack_require__.e(876).then(__webpack_require__.bind(__webpack_require__,"../../node_modules/.aspect_rules_js/raw-loader@4.0.2_524060777/node_modules/raw-loader/dist/cjs.js!../../node_modules/.aspect_rules_js/@player-ui+mocks@0.0.0/node_modules/@player-ui/mocks/action/action-basic.tsx"))}));ReactPlayer.parameters={...ReactPlayer.parameters,docs:{...ReactPlayer.parameters?.docs,source:{originalSource:'createDSLStory(() => import("!!raw-loader!@player-ui/mocks/action/action-basic.tsx"))',...ReactPlayer.parameters?.docs?.source}}};const __namedExportsOrder=["ReactPlayer"]},"./src/ReactPlayer.mdx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js"),_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__("../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js"),_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs"),_Player_stories__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__("./src/Player.stories.tsx"),_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__("./src/ManagedPlayer.stories.tsx");function _createMdxContent(props){const _components=Object.assign({h1:"h1",p:"p",code:"code",pre:"pre",h2:"h2",em:"em",h3:"h3"},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__.RP)(),props.components);return(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment,{children:[(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.W8,{title:"React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h1,{id:"react-player",children:"React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This shows the most basic way of rendering Player in a React app."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Create a React Player using the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"useReactPlayer"})," hook, pass it a flow to render, and use the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ReactPlayer.Component"})," to render the component in your app:"]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.pre,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{className:"language-tsx",children:"const App = () => {\n const { reactPlayer } = useReactPlayer({\n plugins: [new ReferenceAssetsPlugin()],\n });\n\n React.useEffect(() => {\n reactPlayer.start(/** Add content here */);\n }, []);\n\n return ;\n};\n"})}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_Player_stories__WEBPACK_IMPORTED_MODULE_3__.ReactPlayer}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h1,{id:"managed-react-player",children:"Managed React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"The Managed Player is a way of orchestrating chained flows through a React Player.\nIt will step through each flow in the sequence, and enable users to respond after each one."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Between loading each flow, the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ManagedPlayer"})," will trigger ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"React.Suspense"})," and wait for the next flow to be returned."]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.SimpleFlow}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h2,{id:"error-handling",children:"Error Handling"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Within Player there are 2 ways of triggering an error, either sent from the UI (custom assets) or from the core Player library (invalid expressions, etc).\nIn either scenario, the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ManagedPlayer"})," will trigger a ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"fallbackComponent"})," that can be used to render an error screen. The ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"fallbackComponent"})," gets props for either ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.em,{children:"restarting"})," the current flow, which will retry the last flow that Player ran, or ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.em,{children:"resetting"})," the current series of flows (starting over from the very begining)"]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h3,{id:"content-errors",children:"Content Errors"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This is an example of a flow that triggers an error with an issue within the content itself -- in this case trying to evaluate an invalid expression."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.ContentErrorHandling}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h3,{id:"asset-errors",children:"Asset Errors"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This is an example of a flow that triggers an error from the UI -- in this case trying to render as asset type that has no registered handler."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.AssetErrorHandling})]})}const __WEBPACK_DEFAULT_EXPORT__=function MDXContent(props={}){const{wrapper:MDXLayout}=Object.assign({},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__.RP)(),props.components);return MDXLayout?(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(MDXLayout,Object.assign({},props,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_createMdxContent,props)})):_createMdxContent(props)}},"../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{var _chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/chunk-JWY6Y6NU.mjs"),_storybook_global__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("@storybook/global"),_storybook_preview_api__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("@storybook/preview-api"),{window:globalWindow}=(__webpack_require__("@storybook/client-logger"),_storybook_global__WEBPACK_IMPORTED_MODULE_1__.global);globalWindow&&(globalWindow.STORYBOOK_ENV="react");var api=(0,_storybook_preview_api__WEBPACK_IMPORTED_MODULE_2__.start)(_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.o,{render:_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.X});api.forceReRender,api.clientApi.raw;_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.X},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js":(__unused_webpack_module,exports,__webpack_require__)=>{var f=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};function q(c,a,g){var b,d={},e=null,h=null;for(b in void 0!==g&&(e=""+g),void 0!==a.key&&(e=""+a.key),void 0!==a.ref&&(h=a.ref),a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l,exports.jsx=q,exports.jsxs=q},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js":(module,__unused_webpack_exports,__webpack_require__)=>{module.exports=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js")}}]); \ No newline at end of file +/*! For license information please see ReactPlayer-mdx.c828b3f1.iframe.bundle.js.LICENSE.txt */ +"use strict";(self.webpackChunkstorybook_docs=self.webpackChunkstorybook_docs||[]).push([[157,345,944],{"../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{BN:()=>MDXContext,RP:()=>useMDXComponents,gz:()=>withMDXComponents,xA:()=>MDXProvider});var react__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");const MDXContext=react__WEBPACK_IMPORTED_MODULE_0__.createContext({});function withMDXComponents(Component){return function boundMDXComponent(props){const allComponents=useMDXComponents(props.components);return react__WEBPACK_IMPORTED_MODULE_0__.createElement(Component,{...props,allComponents})}}function useMDXComponents(components){const contextComponents=react__WEBPACK_IMPORTED_MODULE_0__.useContext(MDXContext);return react__WEBPACK_IMPORTED_MODULE_0__.useMemo((()=>"function"==typeof components?components(contextComponents):{...contextComponents,...components}),[contextComponents,components])}const emptyObject={};function MDXProvider({components,children,disableParentContext}){let allComponents;return allComponents=disableParentContext?"function"==typeof components?components({}):components||emptyObject:useMDXComponents(components),react__WEBPACK_IMPORTED_MODULE_0__.createElement(MDXContext.Provider,{value:allComponents},children)}},"../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{W8:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.W8,gG:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.gG});__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/chunk-HLWAVYOI.mjs");var _storybook_blocks__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+blocks@7.6.19_830451985/node_modules/@storybook/blocks/dist/index.mjs")},"./src/ManagedPlayer.stories.tsx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{AssetErrorHandling:()=>AssetErrorHandling,ContentErrorHandling:()=>ContentErrorHandling,SimpleFlow:()=>SimpleFlow,__namedExportsOrder:()=>__namedExportsOrder,default:()=>ManagedPlayer_stories});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var dist=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+storybook@0.0.0/node_modules/@player-ui/storybook/dist/index.mjs"),_player_ui_react_dist=(__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs"),__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+react@0.0.0/node_modules/@player-ui/react/dist/index.mjs")),reference_assets_plugin_react_dist=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+reference-assets-plugin-react@0.0.0/node_modules/@player-ui/reference-assets-plugin-react/dist/index.mjs");const first_flow_namespaceObject=JSON.parse('{"id":"flow_1","views":[{"id":"first_view","type":"action","value":"end","label":{"asset":{"id":"next-view-label","type":"text","value":"End Flow 1"}}}],"navigation":{"BEGIN":"flow_1","flow_1":{"startState":"view_1","view_1":{"state_type":"VIEW","ref":"first_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}');function _typeof(o){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},_typeof(o)}function _regeneratorRuntime(){_regeneratorRuntime=function _regeneratorRuntime(){return e};var t,e={},r=Object.prototype,n=r.hasOwnProperty,o=Object.defineProperty||function(t,e,r){t[e]=r.value},i="function"==typeof Symbol?Symbol:{},a=i.iterator||"@@iterator",c=i.asyncIterator||"@@asyncIterator",u=i.toStringTag||"@@toStringTag";function define(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{define({},"")}catch(t){define=function define(t,e,r){return t[e]=r}}function wrap(t,e,r,n){var i=e&&e.prototype instanceof Generator?e:Generator,a=Object.create(i.prototype),c=new Context(n||[]);return o(a,"_invoke",{value:makeInvokeMethod(t,r,c)}),a}function tryCatch(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}e.wrap=wrap;var h="suspendedStart",l="suspendedYield",f="executing",s="completed",y={};function Generator(){}function GeneratorFunction(){}function GeneratorFunctionPrototype(){}var p={};define(p,a,(function(){return this}));var d=Object.getPrototypeOf,v=d&&d(d(values([])));v&&v!==r&&n.call(v,a)&&(p=v);var g=GeneratorFunctionPrototype.prototype=Generator.prototype=Object.create(p);function defineIteratorMethods(t){["next","throw","return"].forEach((function(e){define(t,e,(function(t){return this._invoke(e,t)}))}))}function AsyncIterator(t,e){function invoke(r,o,i,a){var c=tryCatch(t[r],t,o);if("throw"!==c.type){var u=c.arg,h=u.value;return h&&"object"==_typeof(h)&&n.call(h,"__await")?e.resolve(h.__await).then((function(t){invoke("next",t,i,a)}),(function(t){invoke("throw",t,i,a)})):e.resolve(h).then((function(t){u.value=t,i(u)}),(function(t){return invoke("throw",t,i,a)}))}a(c.arg)}var r;o(this,"_invoke",{value:function value(t,n){function callInvokeWithMethodAndArg(){return new e((function(e,r){invoke(t,n,e,r)}))}return r=r?r.then(callInvokeWithMethodAndArg,callInvokeWithMethodAndArg):callInvokeWithMethodAndArg()}})}function makeInvokeMethod(e,r,n){var o=h;return function(i,a){if(o===f)throw Error("Generator is already running");if(o===s){if("throw"===i)throw a;return{value:t,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=maybeInvokeDelegate(c,n);if(u){if(u===y)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===h)throw o=s,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=f;var p=tryCatch(e,r,n);if("normal"===p.type){if(o=n.done?s:l,p.arg===y)continue;return{value:p.arg,done:n.done}}"throw"===p.type&&(o=s,n.method="throw",n.arg=p.arg)}}}function maybeInvokeDelegate(e,r){var n=r.method,o=e.iterator[n];if(o===t)return r.delegate=null,"throw"===n&&e.iterator.return&&(r.method="return",r.arg=t,maybeInvokeDelegate(e,r),"throw"===r.method)||"return"!==n&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+n+"' method")),y;var i=tryCatch(o,e.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,y;var a=i.arg;return a?a.done?(r[e.resultName]=a.value,r.next=e.nextLoc,"return"!==r.method&&(r.method="next",r.arg=t),r.delegate=null,y):a:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,y)}function pushTryEntry(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function resetTryEntry(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function Context(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(pushTryEntry,this),this.reset(!0)}function values(e){if(e||""===e){var r=e[a];if(r)return r.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,i=function next(){for(;++o=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return handle("end");if(i.tryLoc<=this.prev){var c=n.call(i,"catchLoc"),u=n.call(i,"finallyLoc");if(c&&u){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),resetTryEntry(r),y}},catch:function _catch(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;resetTryEntry(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function delegateYield(e,r,n){return this.delegate={iterator:values(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),y}},e}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}var SIMPLE_FLOWS=[first_flow_namespaceObject,JSON.parse('{"id":"flow_2","views":[{"id":"second_view","type":"action","value":"flow_2","label":{"asset":{"id":"next-view-label","type":"text","value":"End View 2"}}}],"navigation":{"BEGIN":"flow_2","flow_2":{"startState":"view_2","view_2":{"state_type":"VIEW","ref":"second_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')],ERROR_CONTENT_FLOW=[first_flow_namespaceObject,JSON.parse('{"id":"flow_2","views":[{"id":"second_view","type":"action","exp":"{{foo.bar..}","value":"end","label":{"asset":{"id":"next-view-label","type":"text","value":"End View 2"}}}],"navigation":{"BEGIN":"flow_2","flow_2":{"startState":"view_2","view_2":{"state_type":"VIEW","ref":"second_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')],ERROR_ASSET_FLOW=[first_flow_namespaceObject,JSON.parse('{"id":"flow_3","views":[{"id":"third_view","type":"error"}],"navigation":{"BEGIN":"flow_3","flow_3":{"startState":"view_3","view_3":{"state_type":"VIEW","ref":"third_view","transitions":{"*":"END_Done"}},"END_Done":{"state_type":"END","outcome":"done"}}}}')];function createFlowManager(flowSequence){var dummyManager={next:function next(prevState){return function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}(_regeneratorRuntime().mark((function _callee(){var flowIndex;return _regeneratorRuntime().wrap((function _callee$(_context){for(;;)switch(_context.prev=_context.next){case 0:if(prevState){_context.next=2;break}return _context.abrupt("return",{value:flowSequence[0]});case 2:if(!((flowIndex=flowSequence.indexOf(prevState.flow))>=flowSequence.length-1)){_context.next=5;break}return _context.abrupt("return",{done:!0});case 5:return _context.abrupt("return",{value:flowSequence[flowIndex+1]});case 6:case"end":return _context.stop()}}),_callee)})))()}};return dummyManager}var jsx_runtime=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js");const ManagedPlayer_stories={title:"React Player/Managed Player"};var SimpleFlow={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(SIMPLE_FLOWS)})})}},ContentErrorHandling={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(ERROR_CONTENT_FLOW)})})}},AssetErrorHandling={render:function render(){return(0,jsx_runtime.jsx)(dist.U2,{children:(0,jsx_runtime.jsx)(_player_ui_react_dist.ManagedPlayer,{plugins:[new reference_assets_plugin_react_dist.w1],manager:createFlowManager(ERROR_ASSET_FLOW)})})}};SimpleFlow.parameters={...SimpleFlow.parameters,docs:{...SimpleFlow.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...SimpleFlow.parameters?.docs?.source}}},ContentErrorHandling.parameters={...ContentErrorHandling.parameters,docs:{...ContentErrorHandling.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...ContentErrorHandling.parameters?.docs?.source}}},AssetErrorHandling.parameters={...AssetErrorHandling.parameters,docs:{...AssetErrorHandling.parameters?.docs,source:{originalSource:"{\n render: () => {\n return \n \n ;\n }\n}",...AssetErrorHandling.parameters?.docs?.source}}};const __namedExportsOrder=["SimpleFlow","ContentErrorHandling","AssetErrorHandling"]},"./src/Player.stories.tsx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{ReactPlayer:()=>ReactPlayer,__namedExportsOrder:()=>__namedExportsOrder,default:()=>__WEBPACK_DEFAULT_EXPORT__});var _player_ui_storybook__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@player-ui+storybook@0.0.0/node_modules/@player-ui/storybook/dist/index.mjs");__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs");const __WEBPACK_DEFAULT_EXPORT__={title:"React Player"};var ReactPlayer=(0,_player_ui_storybook__WEBPACK_IMPORTED_MODULE_1__.D1)((function(){return __webpack_require__.e(876).then(__webpack_require__.bind(__webpack_require__,"../../node_modules/.aspect_rules_js/raw-loader@4.0.2_524060777/node_modules/raw-loader/dist/cjs.js!../../node_modules/.aspect_rules_js/@player-ui+mocks@0.0.0/node_modules/@player-ui/mocks/action/action-basic.tsx"))}));ReactPlayer.parameters={...ReactPlayer.parameters,docs:{...ReactPlayer.parameters?.docs,source:{originalSource:'createDSLStory(() => import("!!raw-loader!@player-ui/mocks/action/action-basic.tsx"))',...ReactPlayer.parameters?.docs?.source}}};const __namedExportsOrder=["ReactPlayer"]},"./src/ReactPlayer.mdx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js"),_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__("../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js"),_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs"),_Player_stories__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__("./src/Player.stories.tsx"),_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__("./src/ManagedPlayer.stories.tsx");function _createMdxContent(props){const _components=Object.assign({h1:"h1",p:"p",code:"code",pre:"pre",h2:"h2",em:"em",h3:"h3"},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__.RP)(),props.components);return(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment,{children:[(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.W8,{title:"React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h1,{id:"react-player",children:"React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This shows the most basic way of rendering Player in a React app."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Create a React Player using the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"useReactPlayer"})," hook, pass it a flow to render, and use the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ReactPlayer.Component"})," to render the component in your app:"]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.pre,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{className:"language-tsx",children:"const App = () => {\n const { reactPlayer } = useReactPlayer({\n plugins: [new ReferenceAssetsPlugin()],\n });\n\n React.useEffect(() => {\n reactPlayer.start(/** Add content here */);\n }, []);\n\n return ;\n};\n"})}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_Player_stories__WEBPACK_IMPORTED_MODULE_3__.ReactPlayer}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h1,{id:"managed-react-player",children:"Managed React Player"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"The Managed Player is a way of orchestrating chained flows through a React Player.\nIt will step through each flow in the sequence, and enable users to respond after each one."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Between loading each flow, the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ManagedPlayer"})," will trigger ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"React.Suspense"})," and wait for the next flow to be returned."]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.SimpleFlow}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h2,{id:"error-handling",children:"Error Handling"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(_components.p,{children:["Within Player there are 2 ways of triggering an error, either sent from the UI (custom assets) or from the core Player library (invalid expressions, etc).\nIn either scenario, the ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"ManagedPlayer"})," will trigger a ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"fallbackComponent"})," that can be used to render an error screen. The ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.code,{children:"fallbackComponent"})," gets props for either ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.em,{children:"restarting"})," the current flow, which will retry the last flow that Player ran, or ",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.em,{children:"resetting"})," the current series of flows (starting over from the very begining)"]}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h3,{id:"content-errors",children:"Content Errors"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This is an example of a flow that triggers an error with an issue within the content itself -- in this case trying to evaluate an invalid expression."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.ContentErrorHandling}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.h3,{id:"asset-errors",children:"Asset Errors"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This is an example of a flow that triggers an error from the UI -- in this case trying to render as asset type that has no registered handler."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.gG,{of:_ManagedPlayer_stories__WEBPACK_IMPORTED_MODULE_4__.AssetErrorHandling})]})}const __WEBPACK_DEFAULT_EXPORT__=function MDXContent(props={}){const{wrapper:MDXLayout}=Object.assign({},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_5__.RP)(),props.components);return MDXLayout?(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(MDXLayout,Object.assign({},props,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_createMdxContent,props)})):_createMdxContent(props)}},"../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{var _chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+react@7.6.19_757876607/node_modules/@storybook/react/dist/chunk-JWY6Y6NU.mjs"),_storybook_global__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("@storybook/global"),_storybook_preview_api__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("@storybook/preview-api"),{window:globalWindow}=(__webpack_require__("@storybook/client-logger"),_storybook_global__WEBPACK_IMPORTED_MODULE_1__.global);globalWindow&&(globalWindow.STORYBOOK_ENV="react");var api=(0,_storybook_preview_api__WEBPACK_IMPORTED_MODULE_2__.start)(_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.o,{render:_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.X});api.forceReRender,api.clientApi.raw;_chunk_JWY6Y6NU_mjs__WEBPACK_IMPORTED_MODULE_0__.X},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js":(__unused_webpack_module,exports,__webpack_require__)=>{var f=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};function q(c,a,g){var b,d={},e=null,h=null;for(b in void 0!==g&&(e=""+g),void 0!==a.key&&(e=""+a.key),void 0!==a.ref&&(h=a.ref),a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l,exports.jsx=q,exports.jsxs=q},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js":(module,__unused_webpack_exports,__webpack_require__)=>{module.exports=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js")}}]); \ No newline at end of file diff --git a/next/storybook-demo/ReactPlayer-mdx.657a80aa.iframe.bundle.js.LICENSE.txt b/next/storybook-demo/ReactPlayer-mdx.c828b3f1.iframe.bundle.js.LICENSE.txt similarity index 100% rename from next/storybook-demo/ReactPlayer-mdx.657a80aa.iframe.bundle.js.LICENSE.txt rename to next/storybook-demo/ReactPlayer-mdx.c828b3f1.iframe.bundle.js.LICENSE.txt diff --git a/next/storybook-demo/Welcome-mdx.82ec2157.iframe.bundle.js b/next/storybook-demo/Welcome-mdx.e70b16db.iframe.bundle.js similarity index 96% rename from next/storybook-demo/Welcome-mdx.82ec2157.iframe.bundle.js rename to next/storybook-demo/Welcome-mdx.e70b16db.iframe.bundle.js index 4d28f72c..7c30158e 100644 --- a/next/storybook-demo/Welcome-mdx.82ec2157.iframe.bundle.js +++ b/next/storybook-demo/Welcome-mdx.e70b16db.iframe.bundle.js @@ -1,2 +1,2 @@ -/*! For license information please see Welcome-mdx.82ec2157.iframe.bundle.js.LICENSE.txt */ -"use strict";(self.webpackChunkstorybook_docs=self.webpackChunkstorybook_docs||[]).push([[555],{"../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{BN:()=>MDXContext,RP:()=>useMDXComponents,gz:()=>withMDXComponents,xA:()=>MDXProvider});var react__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");const MDXContext=react__WEBPACK_IMPORTED_MODULE_0__.createContext({});function withMDXComponents(Component){return function boundMDXComponent(props){const allComponents=useMDXComponents(props.components);return react__WEBPACK_IMPORTED_MODULE_0__.createElement(Component,{...props,allComponents})}}function useMDXComponents(components){const contextComponents=react__WEBPACK_IMPORTED_MODULE_0__.useContext(MDXContext);return react__WEBPACK_IMPORTED_MODULE_0__.useMemo((()=>"function"==typeof components?components(contextComponents):{...contextComponents,...components}),[contextComponents,components])}const emptyObject={};function MDXProvider({components,children,disableParentContext}){let allComponents;return allComponents=disableParentContext?"function"==typeof components?components({}):components||emptyObject:useMDXComponents(components),react__WEBPACK_IMPORTED_MODULE_0__.createElement(MDXContext.Provider,{value:allComponents},children)}},"../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{W8:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.W8,gG:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.gG});__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/chunk-HLWAVYOI.mjs");var _storybook_blocks__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+blocks@7.6.19_830451985/node_modules/@storybook/blocks/dist/index.mjs")},"./src/Welcome.mdx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js"),_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__("../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js"),_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs");function _createMdxContent(props){const _components=Object.assign({p:"p",img:"img"},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__.RP)(),props.components);return(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment,{children:[(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.W8,{title:"Welcome"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.img,{src:"https://player-ui.github.io/latest/logo/logo-light-large.png",alt:"Player Logo"})}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"Player is a framework for building cross-platform experiences. The main engine is written in Typescript, with platform specific adapters for iOS, Android, and React (web). It's built from the ground up with plugins in mind to perfectly tailor the experience specifically for your app."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This storybook focuses on some example of using the React Player library, including some of the underlying plugins and references for building out apps using Player."})]})}const __WEBPACK_DEFAULT_EXPORT__=function MDXContent(props={}){const{wrapper:MDXLayout}=Object.assign({},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6458_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__.RP)(),props.components);return MDXLayout?(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(MDXLayout,Object.assign({},props,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_createMdxContent,props)})):_createMdxContent(props)}},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js":(__unused_webpack_module,exports,__webpack_require__)=>{var f=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};function q(c,a,g){var b,d={},e=null,h=null;for(b in void 0!==g&&(e=""+g),void 0!==a.key&&(e=""+a.key),void 0!==a.ref&&(h=a.ref),a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l,exports.jsx=q,exports.jsxs=q},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js":(module,__unused_webpack_exports,__webpack_require__)=>{module.exports=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js")}}]); \ No newline at end of file +/*! For license information please see Welcome-mdx.e70b16db.iframe.bundle.js.LICENSE.txt */ +"use strict";(self.webpackChunkstorybook_docs=self.webpackChunkstorybook_docs||[]).push([[555],{"../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{BN:()=>MDXContext,RP:()=>useMDXComponents,gz:()=>withMDXComponents,xA:()=>MDXProvider});var react__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");const MDXContext=react__WEBPACK_IMPORTED_MODULE_0__.createContext({});function withMDXComponents(Component){return function boundMDXComponent(props){const allComponents=useMDXComponents(props.components);return react__WEBPACK_IMPORTED_MODULE_0__.createElement(Component,{...props,allComponents})}}function useMDXComponents(components){const contextComponents=react__WEBPACK_IMPORTED_MODULE_0__.useContext(MDXContext);return react__WEBPACK_IMPORTED_MODULE_0__.useMemo((()=>"function"==typeof components?components(contextComponents):{...contextComponents,...components}),[contextComponents,components])}const emptyObject={};function MDXProvider({components,children,disableParentContext}){let allComponents;return allComponents=disableParentContext?"function"==typeof components?components({}):components||emptyObject:useMDXComponents(components),react__WEBPACK_IMPORTED_MODULE_0__.createElement(MDXContext.Provider,{value:allComponents},children)}},"../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{W8:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.W8,gG:()=>_storybook_blocks__WEBPACK_IMPORTED_MODULE_1__.gG});__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/chunk-HLWAVYOI.mjs");var _storybook_blocks__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+blocks@7.6.19_830451985/node_modules/@storybook/blocks/dist/index.mjs")},"./src/Welcome.mdx":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js");var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js"),_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__("../../node_modules/.aspect_rules_js/@mdx-js+react@2.3.0_react_18.3.1/node_modules/@mdx-js/react/lib/index.js"),_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__("../../node_modules/.aspect_rules_js/@storybook+addon-docs@7.6.19_830451985/node_modules/@storybook/addon-docs/dist/index.mjs");function _createMdxContent(props){const _components=Object.assign({p:"p",img:"img"},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__.RP)(),props.components);return(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment,{children:[(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_storybook_addon_docs__WEBPACK_IMPORTED_MODULE_2__.W8,{title:"Welcome"}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.img,{src:"https://player-ui.github.io/latest/logo/logo-light-large.png",alt:"Player Logo"})}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"Player is a framework for building cross-platform experiences. The main engine is written in Typescript, with platform specific adapters for iOS, Android, and React (web). It's built from the ground up with plugins in mind to perfectly tailor the experience specifically for your app."}),"\n",(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_components.p,{children:"This storybook focuses on some example of using the React Player library, including some of the underlying plugins and references for building out apps using Player."})]})}const __WEBPACK_DEFAULT_EXPORT__=function MDXContent(props={}){const{wrapper:MDXLayout}=Object.assign({},(0,_home_circleci_cache_bazel_bazel_circleci_e8362d362e14c7d23506d1dfa3aea8b8_sandbox_processwrapper_sandbox_6459_execroot_main_bazel_out_k8_fastbuild_bin_node_modules_aspect_rules_js_storybook_addon_docs_7_6_19_830451985_node_modules_storybook_addon_docs_dist_shims_mdx_react_shim__WEBPACK_IMPORTED_MODULE_3__.RP)(),props.components);return MDXLayout?(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(MDXLayout,Object.assign({},props,{children:(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(_createMdxContent,props)})):_createMdxContent(props)}},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js":(__unused_webpack_module,exports,__webpack_require__)=>{var f=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/index.js"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};function q(c,a,g){var b,d={},e=null,h=null;for(b in void 0!==g&&(e=""+g),void 0!==a.key&&(e=""+a.key),void 0!==a.ref&&(h=a.ref),a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l,exports.jsx=q,exports.jsxs=q},"../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/jsx-runtime.js":(module,__unused_webpack_exports,__webpack_require__)=>{module.exports=__webpack_require__("../../node_modules/.aspect_rules_js/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js")}}]); \ No newline at end of file diff --git a/next/storybook-demo/Welcome-mdx.82ec2157.iframe.bundle.js.LICENSE.txt b/next/storybook-demo/Welcome-mdx.e70b16db.iframe.bundle.js.LICENSE.txt similarity index 100% rename from next/storybook-demo/Welcome-mdx.82ec2157.iframe.bundle.js.LICENSE.txt rename to next/storybook-demo/Welcome-mdx.e70b16db.iframe.bundle.js.LICENSE.txt diff --git a/next/storybook-demo/iframe.html b/next/storybook-demo/iframe.html index cad23049..b51ad1e0 100644 --- a/next/storybook-demo/iframe.html +++ b/next/storybook-demo/iframe.html @@ -348,7 +348,7 @@ window['DOCS_OPTIONS'] = {"defaultName":"Docs","autodocs":"tag"}; - - Skip to content
Skip to content

CLI

The CLI package is a simple way for users to interact with some of the build/validation tooling. As new capabilities are added to the ecosystem, they may be exposed via this cli to use of use for developers.

+

CLI

The CLI package is a simple way for users to interact with some of the build/validation tooling. As new capabilities are added to the ecosystem, they may be exposed via this cli to use of use for developers.

Config

Config files are able to customize the behavior of the CLI commands without requiring args. Behavior specific to execution can leverage plugins, which can be composed together using presets. Full configs can also be shared using extensions.

To resolve a full configuration, the extension is taken as the base, the presets are applied in order, then local plugins. @@ -111,4 +111,4 @@

Commands

  • player dependency-versions check Checks for @player-ui/@player-tools dependency version mismatches and issues warnings/solutions accordingly
  • -
    USAGE
    $ player dependency-versions check [-c <value>] [-v] [-p] [-i <value>]
    FLAGS
    -c, --config=<value> Path to a specific config file to load.
    By default, will automatically search for an rc or config file to load
    -i, --ignore=<value>... Ignore the specified pattern(s) when outputting results. Note multiple patterns can be passed
    -p, --path Outputs full path to dependency
    -v, --verbose Give verbose description
    DESCRIPTION
    Checks for `@player-ui/@player-tools` dependency version mismatches and issues warnings/solutions accordingly
    Consider the following:
    - The interpretation of TOP-LEVEL and NESTED dependencies is as follows:
    a. TOP-LEVEL dependencies only have one 'node_modules' in their path
    b. NESTED dependencies have more than one 'node_modules' in their path
    - `@player-ui/@player-tools` dependencies are fetched not only from inside the 'node_modules' at the top of the repository in which it is run but also from 'node_modules' in sub-directories.
    For example, if you have some 'node_modules' inside of a 'packages' folder that contains `@player-ui/@player-tools` dependencies, then these will also be fetched.
    The display of such dependencies also depends on the first bullet point.
    \ No newline at end of file +
    USAGE
    $ player dependency-versions check [-c <value>] [-v] [-p] [-i <value>]
    FLAGS
    -c, --config=<value> Path to a specific config file to load.
    By default, will automatically search for an rc or config file to load
    -i, --ignore=<value>... Ignore the specified pattern(s) when outputting results. Note multiple patterns can be passed
    -p, --path Outputs full path to dependency
    -v, --verbose Give verbose description
    DESCRIPTION
    Checks for `@player-ui/@player-tools` dependency version mismatches and issues warnings/solutions accordingly
    Consider the following:
    - The interpretation of TOP-LEVEL and NESTED dependencies is as follows:
    a. TOP-LEVEL dependencies only have one 'node_modules' in their path
    b. NESTED dependencies have more than one 'node_modules' in their path
    - `@player-ui/@player-tools` dependencies are fetched not only from inside the 'node_modules' at the top of the repository in which it is run but also from 'node_modules' in sub-directories.
    For example, if you have some 'node_modules' inside of a 'packages' folder that contains `@player-ui/@player-tools` dependencies, then these will also be fetched.
    The display of such dependencies also depends on the first bullet point.
    \ No newline at end of file diff --git a/next/tools/dsl-content-playground/index.html b/next/tools/dsl-content-playground/index.html index d97c1d59..f3410a35 100644 --- a/next/tools/dsl-content-playground/index.html +++ b/next/tools/dsl-content-playground/index.html @@ -1,4 +1,4 @@ - DSL Content Playground | Player - - Skip to content
    Skip to content

    DSL Content Playground

    +

    DSL Content Playground

    Content can be authored using JSON, or (preferred) with a TypeScript/React API. Below is a sample editor where you can explore authoring content using the DSL and our reference assets. -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/next/tools/storybook/index.html b/next/tools/storybook/index.html index 321892af..44103b71 100644 --- a/next/tools/storybook/index.html +++ b/next/tools/storybook/index.html @@ -1,4 +1,4 @@ - Player + Storybook Integration | Player - Skip to content
    Skip to content

    Player + Storybook Integration

    Reference Assets

    -

    The reference asset set, complete with the storybook-plugin integration below is available here

    +

    Player + Storybook Integration

    Reference Assets

    +

    The reference asset set, complete with the storybook-plugin integration below is available here

    Storybook Plugin

    The @player-ui/storybook package is a storybook addon + wrapper that provides easy-to-use mechanisms for integrating Player flows into storybook.

    Installation

    There are a few different parts required to integrate with storybook.

    Start by installing @player-ui/storybook:

    -
    Terminal window
    yarn add @player-ui/storybook
    +
    Terminal window
    npm i @player-ui/storybook

    Next, add @player-ui/storybook to the addons section in .storybook/main.js

    -
    module.exports = {
    addons: ["@player-ui/storybook"],
    };
    +
    module.exports = {
    addons: ["@player-ui/storybook"],
    };

    In .storybook/preview.js add the PlayerDecorator:

    import { PlayerDecorator } from "@player-ui/storybook";
    export const decorators = [PlayerDecorator];
    @@ -105,4 +133,4 @@

    Asset Docs

    const meta: Meta<typeof Action> = {
    title: "Reference Assets/Action",
    component: Action,
    parameters: {
    assetDocs: ["ActionAsset"],
    },
    };

    Reset

    The reset button in the toolbar will reset the running Player with the initial content. This is useful for clearing any data or validation state, or for resetting a completed flow.

    -
    Flow Reset Button
    \ No newline at end of file +
    Flow Reset Button
    \ No newline at end of file diff --git a/next/tools/view-ast-explorer/index.html b/next/tools/view-ast-explorer/index.html index 71059c1e..6fbfd242 100644 --- a/next/tools/view-ast-explorer/index.html +++ b/next/tools/view-ast-explorer/index.html @@ -1,4 +1,4 @@ - View AST Explorer | Player - - Skip to content
    Skip to content

    View AST Explorer

    +

    View AST Explorer

    The internal representation of the View parses into an AST structure. This is often used during the transform phase to edit the content prior to rendering. Below is an interactive way of previewing the internal AST representation for the given content. -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/next/xlr/concepts/index.html b/next/xlr/concepts/index.html index 0570c65f..305b959b 100644 --- a/next/xlr/concepts/index.html +++ b/next/xlr/concepts/index.html @@ -1,4 +1,4 @@ - XLR Definitions | Player - - Skip to content
    Skip to content

    XLR Definitions

    When talking about anything its helpful to make sure everyone is on the same page, XLR is no exception. In this section we’ll explore some concepts related to XLRs, how they work, and how they’re used.

    +

    XLR Definitions

    When talking about anything its helpful to make sure everyone is on the same page, XLR is no exception. In this section we’ll explore some concepts related to XLRs, how they work, and how they’re used.

    Capability

    When we talk about a Capability, we are essentially talking about what it provides to Player. Most, if not all, capabilities are provided by Plugins. Capabilities are described in the manifest file in the xlr folder of a distribution. The manifest file, provided as both a .json and a .js file for static or dynamic use, contains the mapping of capabilities to a list of the XLRs.

    XLR Objects

    @@ -80,4 +80,4 @@

    Named Types

    XLR SDK

    The XLR SDK is used to abstract away the more tedious interactions XLRs like loading them from their package, managing them when they’re loaded, and validating content against them. The SDK does include an simple object store so that it can be used out of the box, however if your use case requires some different logic it can be extended quite easily. In fact, we do that in the Player LSP.

    Transform Functions

    -

    Transform functions can be used to modify XLRs when they’re loaded and when they’re exported. There is no real limit to what you can do in a transform function but typical use cases are things like adding new properties to object and substituting type references with different ones.

    \ No newline at end of file +

    Transform functions can be used to modify XLRs when they’re loaded and when they’re exported. There is no real limit to what you can do in a transform function but typical use cases are things like adding new properties to object and substituting type references with different ones.

    \ No newline at end of file diff --git a/next/xlr/intro/index.html b/next/xlr/intro/index.html index edf7f7bf..75d8de3d 100644 --- a/next/xlr/intro/index.html +++ b/next/xlr/intro/index.html @@ -1,4 +1,4 @@ - Into to XLR | Player - - Skip to content
    Skip to content

    Into to XLR

    What is XLR

    +

    Into to XLR

    What is XLR

    XLR, short for cross(x) language representation, is a way to export a static and language agnostic description of a TypeScript type or interface packaged along with the original type or interface. XLR started as a fork of core-types to add support for some missing features (generics, supported types, inheritance) that are heavily used in Player but has evolved into a superset of its features with some opinionated functionality to support Player specific conventions. XLR now powers a majority of the Player language features such as content validation, editor suggestions, and in editor documentation.

    Why Do We Need it

    XLR was developed to fill in one of the major gaps in the Player ecosystem: Because everything is dynamic, there is no way to statically/programmatically know what plugin provides what capabilities (assets, data types, validations, etc) to Player and what that capability looks like. This becomes a real issue in the tooling around Player when things like content authoring and validation. In both use-cases information on what assets are available for use, what parameters do those assets require, what types are those parameters, etc. is required but cannot be assumed. Historically we have seen this gap bridged by hardcoding the set of capabilities that are available but that has proven to be a never ending cycle of keeping those definitions updated when things change and knowing about the entire set of capabilities, even those that people develop silently. With XLR, a static description of all capabilities provided by a plugin can be compiled at build time, included in the package, and used at any point in the future for a multitude of capabilities.

    How Do I Leverage XLR

    How much you use XLR is pretty dependant on your niche in the Player ecosystem.

    As a TypeScript Plugin/Capability Author

    -

    If you are developing Player capabilities for Core/React, all you need to do is have your Plugin fill in the ExtendedPlayerPlugin interface and run the Player CLI during build time and the CLI will take care of compiling and bundling the capabilities. You can see an example of this in the Exporting Plugin Capabilities section.

    +

    If you are developing Player capabilities for Core/React, all you need to do is have your Plugin fill in the ExtendedPlayerPlugin interface and run the Player CLI during build time and the CLI will take care of compiling and bundling the capabilities. You can see an example of this in the Exporting Plugin Capabilities section.

    As Someone Building Capabilities Around Player

    -

    If you want to leverage XLRs to develop capabilities around Player, you’ll want to start with the SDK and use it to load the XLRs from published packages. Then, using the SDK, you can access all the loaded types and information about them for use in your application.

    \ No newline at end of file +

    If you want to leverage XLRs to develop capabilities around Player, you’ll want to start with the SDK and use it to load the XLRs from published packages. Then, using the SDK, you can access all the loaded types and information about them for use in your application.

    \ No newline at end of file diff --git a/next/xlr/usage/index.html b/next/xlr/usage/index.html index cf0f5868..3df3ac44 100644 --- a/next/xlr/usage/index.html +++ b/next/xlr/usage/index.html @@ -1,4 +1,4 @@ - Using XLR | Player - - Skip to content
    Skip to content

    Using XLR

    Part 1 - Creating XLRs

    +

    Using XLR

    Part 1 - Creating XLRs

    XLR creation is done through the Player CLI which can be added to your project like so:

    -
    Terminal window
    yarn install @player-tools/cli
    +
    Terminal window
    npm i @player-tools/cli

    Exporting Base Type Definitions

    If you want to compile all exported interfaces/types to XLRs run the following command as part of your build

    -
    Terminal window
    player xlr compile -m types <other options>
    +
    Terminal window
    player xlr compile -m types <other options>

    Exporting Plugin Capabilities

    If you are writing a Player Plugin, you’ll first need to have your plugin extend the ExtendedPlayerPlugin interface and fill in the generics with an array of the interfaces/types for each Capability. For example, you can see how its done below in the core reference assets plugin

    export class ReferenceAssetsPlugin
    implements
    PlayerPlugin,
    ExtendedPlayerPlugin<
    [InputAsset, TextAsset, ActionAsset, InfoAsset, CollectionAsset]
    >
    @@ -82,7 +110,7 @@

    Exporting Plugin Capabilities

    Part 2 - Using XLRs

    SDK: Initialization

    To start using the XLR SDK you’ll need to install the SDK package

    -
    Terminal window
    yarn install @player-tools/xlr-sdk
    +
    Terminal window
    npm i @player-tools/xlr-sdk

    Next, import the SDK

    import { XLRSDK } from "@player-tools/xlr-sdk"

    If you want to implement a custom object store, also import the XLRRegistry interface and have your custom registry implement it.

    @@ -112,4 +140,4 @@

    Type Validation

    const sdk = new XLRSDK();
    ///... Loading XLRs
    const mockAsset = parseTree(`
    {
    "id": 1,
    "type": "input",
    "binding": "some.data",
    "label": {
    "asset": {
    "value": "{{input.label}}"
    }
    }
    `);
    -
    return sdk.validate('InputAsset', mockAsset)
    \ No newline at end of file +
    return sdk.validate('InputAsset', mockAsset)
    \ No newline at end of file