@umami/shiso 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/chunks/App.js +1081 -47
  2. package/dist/chunks/architectureDiagram-5GKGNRK7.js +1 -1
  3. package/dist/chunks/chunk-GMAD6QVW.js +1 -1
  4. package/dist/chunks/cose-bilkent-JH36ORCC.js +1 -1
  5. package/dist/chunks/dist.js +1 -1
  6. package/dist/chunks/docs.js +183 -8
  7. package/dist/chunks/ganttDiagram-EL5Y4UJY.js +1 -1
  8. package/dist/chunks/src.js +1 -1
  9. package/dist/components.js +1 -1
  10. package/dist/entry-client.js +1 -1
  11. package/dist/entry-server.js +1 -1
  12. package/docs.schema.json +1164 -1135
  13. package/package.json +1 -2
  14. package/scripts/check-content.mjs +42 -15
  15. package/scripts/expand-openapi-navigation.mjs +47 -21
  16. package/scripts/generate-openapi.mjs +36 -11
  17. package/scripts/generate-search-index.mjs +19 -18
  18. package/scripts/lib/openapi-project.mjs +197 -0
  19. package/scripts/lib/openapi.mjs +257 -111
  20. package/scripts/lib/request-samples.mjs +323 -0
  21. package/scripts/load-docs-config.mjs +23 -15
  22. package/scripts/prerender.mjs +17 -14
  23. package/scripts/vite-docs-config.mjs +1 -0
  24. package/src/components/ApiPlayground.tsx +522 -0
  25. package/src/components/DocContent.tsx +15 -4
  26. package/src/components/Docs.tsx +18 -3
  27. package/src/components/LanguageSwitcher.tsx +26 -30
  28. package/src/components/OpenApiOperation.tsx +58 -12
  29. package/src/components/OpenApiSchema.tsx +97 -0
  30. package/src/components/SideNav.tsx +2 -2
  31. package/src/lib/openapi.generated.ts +2 -1
  32. package/src/lib/openapi.ts +104 -10
  33. package/src/lib/site-model.ts +12 -0
  34. package/src/lib/translations/de.json +26 -1
  35. package/src/lib/translations/en.json +26 -1
  36. package/src/lib/translations/es.json +26 -1
  37. package/src/lib/translations/fr.json +26 -1
  38. package/src/lib/translations/ja.json +26 -1
  39. package/src/lib/translations/zh-Hans.json +26 -1
  40. package/src/lib/translations/zh-Hant.json +26 -1
  41. package/src/lib/types.ts +89 -5
  42. package/types/labels.d.ts +25 -0
  43. package/vite.config.ts +3 -4
  44. package/CHANGELOG.md +0 -8
@@ -1,8 +1,8 @@
1
- import { $ as useFloatingNodeId, $n as jsdom, $t as resolveIcon, A as safePolygon, An as useDirection, Ar as Route, B as attachPreventUnmountOnClose, Bn as isListIndexDisabled, Cn as englishLabels, Cr as X$1, D as useAnchorPositioning, Dn as isValidLocale, Dt as Collapsible, E as getDisabledMountTransitionStyles, En as getTextDirection, Er as createLucideIcon, Et as Expandable, F as createInitialPopupStoreState, Fn as findNonDisabledListIndex, Ft as useCompositeItem, G as useTriggerDataForwarding, Gn as isTypeableCombobox, H as useImplicitActiveTrigger, Hn as isVirtualPointerEvent, I as popupStoreSelectors, In as getMaxListIndex, J as useDismiss, Jn as getTarget, K as useSyncedFloatingRootContext, Kn as activeElement, L as PopupTriggerMap, Ln as getMinListIndex, M as useHoverFloatingInteraction, Mn as getTabbableAfterElement, Mr as Routes, Mt as CodeGroup, N as useFocus, Nn as getTabbableBeforeElement, Nr as useLocation, Nt as COMPOSITE_KEYS, O as popupTransitionStateMapping, On as resolveLocale, Or as Link, Ot as CollapsibleContent, P as usePopupHandleStore, Pn as isOutsideEvent, Pr as useNavigate, Pt as inertValue, Q as FloatingTree, Qn as ARROW_UP, R as FOCUSABLE_POPUP_PROPS, Rn as isElementVisible, Sn as useLabels, Sr as isLastTraversableNode, T as usePositioner, Tn as getLanguageName, Tr as Check, Tt as renderInlineMarkdown, U as useOpenStateTransitions, Un as stopEvent, V as setPopupOpenState, Vn as isVirtualClick, W as usePopupInteractionProps, Wn as getFloatingFocusElement, X as FloatingFocusManager, Xn as ARROW_LEFT, Xt as Badge$1, Y as useClick, Yn as ARROW_DOWN, Yt as Badge, Z as FloatingNode, Zn as ARROW_RIGHT, _n as triggerPress, _r as useStableCallback, a as DialogTitle, an as useAnimationFrame, ar as Button, at as POPUP_COLLISION_AVOIDANCE, br as getWindow, bt as usePanelContent, c as useOpenInteractionType, cn as escapeKey, cr as EMPTY_ARRAY, ct as fastComponent, d as useScrollLock, dn as itemPress, dr as useButton, en as getIcon, er as webkit, et as useFloatingParentNodeId, f as DialogPortal$1, fn as listNavigation, fr as dispatchClickWithModifiers, g as DialogBackdrop, gn as triggerHover, gr as useIsoLayoutEffect, gt as PropertiesTable, h as DialogClose, hn as triggerFocus, hr as mergeProps$1, i as DialogContent$1, in as useCompositeListItem, ir as useTimeout, it as DROPDOWN_COLLISION_AVOIDANCE, j as useHoverReferenceInteraction, jn as getNextTabbable, k as pressableTriggerOpenStateMapping, kn as addEventListener, kr as Navigate, kt as CollapsibleTrigger, l as DialogTitle$1, ln as focusOut, lr as EMPTY_OBJECT, lt as fastComponentRef, m as DialogPopup, mn as siblingOpen, mr as useCompositeRootContext, n as ZoomableImage, nn as useAnimationsFinished, nr as useBaseUiId, nt as FloatingTreeStore, o as DialogTrigger, on as createChangeEventDetails, or as cn, ot as enqueueFocus, p as InternalBackdrop, pn as outsidePress, pr as ownerDocument, q as ReactStore, qn as contains, r as Dialog$1, rn as useValueAsRef, rr as useId$2, rt as FloatingPortal, s as DialogTrigger$1, sn as cancelOpen, sr as useRenderElement, st as FocusGuard, t as docs_exports, tn as useOpenChangeComplete, tr as mac, tt as useFloatingTree, u as DialogRoot, un as imperativeAction, ur as useMergedRefs, vn as CompositeList, vr as useRefWithInit, w as TooltipProvider, wn as resolveLabels, wr as ChevronRight, xn as LabelContext, xr as isHTMLElement, yn as Mermaid, yr as getParentNode, yt as PanelProvider, z as PopupHandleAttachment, zn as isIndexOutOfListBounds } from "./docs.js";
1
+ import { $ as useFloatingNodeId, $n as ARROW_LEFT, $t as resolveIcon, A as safePolygon, An as isValidLocale, Ar as createLucideIcon, B as attachPreventUnmountOnClose, Bn as getMinListIndex, Cr as getParentNode, D as useAnchorPositioning, Dn as resolveLabels, Dr as X$1, Dt as Collapsible, E as getDisabledMountTransitionStyles, En as englishLabels, Er as isLastTraversableNode, Et as Expandable, F as createInitialPopupStoreState, Fn as getTabbableAfterElement, Ft as useCompositeItem, G as useTriggerDataForwarding, Gn as isVirtualPointerEvent, H as useImplicitActiveTrigger, Hn as isIndexOutOfListBounds, I as popupStoreSelectors, In as getTabbableBeforeElement, Ir as Routes, J as useDismiss, Jn as isTypeableCombobox, K as useSyncedFloatingRootContext, Kn as stopEvent, L as PopupTriggerMap, Ln as isOutsideEvent, Lr as useLocation, M as useHoverFloatingInteraction, Mn as addEventListener, Mr as Link, Mt as CodeGroup, N as useFocus, Nn as useDirection, Nr as Navigate, Nt as COMPOSITE_KEYS, O as popupTransitionStateMapping, On as getLanguageName, Or as ChevronRight, Ot as CollapsibleContent, P as usePopupHandleStore, Pn as getNextTabbable, Pr as Route, Pt as inertValue, Q as FloatingTree, Qn as ARROW_DOWN, R as FOCUSABLE_POPUP_PROPS, Rn as findNonDisabledListIndex, Rr as useNavigate, Sn as Mermaid, Sr as useRefWithInit, T as usePositioner, Tn as useLabels, Tr as isHTMLElement, Tt as renderInlineMarkdown, U as useOpenStateTransitions, Un as isListIndexDisabled, V as setPopupOpenState, Vn as isElementVisible, W as usePopupInteractionProps, Wn as isVirtualClick, X as FloatingFocusManager, Xn as contains, Xt as Badge$1, Y as useClick, Yn as activeElement, Yt as Badge, Z as FloatingNode, Zn as getTarget, _n as triggerFocus, _r as ownerDocument, a as DialogTitle, an as useCompositeListItem, ar as useBaseUiId, at as POPUP_COLLISION_AVOIDANCE, bn as CompositeList, br as useIsoLayoutEffect, bt as usePanelContent, c as useOpenInteractionType, cn as createChangeEventDetails, cr as Button, ct as fastComponent, d as useScrollLock, dn as focusOut, dr as EMPTY_ARRAY, en as getIcon, er as ARROW_RIGHT, et as useFloatingParentNodeId, f as DialogPortal$1, fn as imperativeAction, fr as EMPTY_OBJECT, g as DialogBackdrop, gn as siblingOpen, gr as dispatchClickWithModifiers, gt as PropertiesTable, h as DialogClose, hn as outsidePress, hr as useButton, i as DialogContent$1, in as transitionStatusMapping, ir as mac, it as DROPDOWN_COLLISION_AVOIDANCE, j as useHoverReferenceInteraction, jn as resolveLocale, k as pressableTriggerOpenStateMapping, kn as getTextDirection, kr as Check, kt as CollapsibleTrigger, l as DialogTitle$1, ln as cancelOpen, lr as cn, lt as fastComponentRef, m as DialogPopup, mn as listNavigation, mr as useMergedRefs, n as ZoomableImage, nn as useAnimationsFinished, nr as jsdom, nt as FloatingTreeStore, o as DialogTrigger, on as useTransitionStatus, or as useId$2, ot as enqueueFocus, p as InternalBackdrop, pn as itemPress, pr as NOOP, q as ReactStore, qn as getFloatingFocusElement, r as Dialog$1, rn as useValueAsRef, rr as webkit, rt as FloatingPortal, s as DialogTrigger$1, sn as useAnimationFrame, sr as useTimeout, st as FocusGuard, t as docs_exports, tn as useOpenChangeComplete, tr as ARROW_UP, tt as useFloatingTree, u as DialogRoot, un as escapeKey, ur as useRenderElement, vn as triggerHover, vr as useCompositeRootContext, w as TooltipProvider, wn as LabelContext, wr as getWindow, xn as useControlled, xr as useStableCallback, yn as triggerPress, yr as mergeProps$1, yt as PanelProvider, z as PopupHandleAttachment, zn as getMaxListIndex } from "./docs.js";
2
2
  import { n as highlightTerms } from "./search.js";
3
3
  import { resolveSearchProvider } from "../search.js";
4
4
  import * as React from "react";
5
- import { useCallback, useEffect, useRef, useState } from "react";
5
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
6
6
  import "@fontsource-variable/inter/index.css";
7
7
  import "@fontsource/jetbrains-mono/400.css";
8
8
  import "@umami/shiso/styles.css";
@@ -12,7 +12,7 @@ import * as ReactDOM from "react-dom";
12
12
  import shiso from "virtual:shiso-config";
13
13
  import rawConfig from "virtual:shiso-docs-config";
14
14
  import { LAST_MODIFIED } from "@/generated/last-modified";
15
- import { OPENAPI_OPERATIONS } from "@/lib/openapi.generated";
15
+ import { OPENAPI_OPERATIONS, OPENAPI_SCHEMAS } from "@/lib/openapi.generated";
16
16
 
17
17
  //#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/arrow-left.mjs
18
18
  /**
@@ -2172,6 +2172,43 @@ function useMenuItem(params) {
2172
2172
  }), [getItemProps, mergedRef]);
2173
2173
  }
2174
2174
 
2175
+ //#endregion
2176
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/checkbox-item/MenuCheckboxItemDataAttributes.mjs
2177
+ let MenuCheckboxItemDataAttributes = /*#__PURE__*/ function(MenuCheckboxItemDataAttributes) {
2178
+ /**
2179
+ * Present when the menu checkbox item is checked.
2180
+ */
2181
+ MenuCheckboxItemDataAttributes["checked"] = "data-checked";
2182
+ /**
2183
+ * Present when the menu checkbox item is not checked.
2184
+ */
2185
+ MenuCheckboxItemDataAttributes["unchecked"] = "data-unchecked";
2186
+ /**
2187
+ * Present when the menu checkbox item is disabled.
2188
+ */
2189
+ MenuCheckboxItemDataAttributes["disabled"] = "data-disabled";
2190
+ /**
2191
+ * Present when the menu checkbox item is highlighted.
2192
+ */
2193
+ MenuCheckboxItemDataAttributes["highlighted"] = "data-highlighted";
2194
+ return MenuCheckboxItemDataAttributes;
2195
+ }({});
2196
+
2197
+ //#endregion
2198
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/utils/stateAttributesMapping.mjs
2199
+ const itemMapping = {
2200
+ checked(value) {
2201
+ if (value) return { [MenuCheckboxItemDataAttributes.checked]: "" };
2202
+ return { [MenuCheckboxItemDataAttributes.unchecked]: "" };
2203
+ },
2204
+ ...transitionStatusMapping
2205
+ };
2206
+
2207
+ //#endregion
2208
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/group/MenuGroupContext.mjs
2209
+ const MenuGroupContext = /*#__PURE__*/ React.createContext(void 0);
2210
+ MenuGroupContext.displayName = "MenuGroupContext";
2211
+
2175
2212
  //#endregion
2176
2213
  //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/item/MenuItem.mjs
2177
2214
  /**
@@ -2595,6 +2632,187 @@ const MenuPositioner = /*#__PURE__*/ React.forwardRef(function MenuPositioner(co
2595
2632
  });
2596
2633
  MenuPositioner.displayName = "MenuPositioner";
2597
2634
 
2635
+ //#endregion
2636
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/radio-group/MenuRadioGroupContext.mjs
2637
+ const MenuRadioGroupContext = /*#__PURE__*/ React.createContext(void 0);
2638
+ MenuRadioGroupContext.displayName = "MenuRadioGroupContext";
2639
+ function useMenuRadioGroupContext() {
2640
+ const context = React.useContext(MenuRadioGroupContext);
2641
+ if (context === void 0) throw new Error("Base UI: MenuRadioGroupContext is missing. MenuRadioGroup parts must be placed within <Menu.RadioGroup>.");
2642
+ return context;
2643
+ }
2644
+
2645
+ //#endregion
2646
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/radio-group/MenuRadioGroup.mjs
2647
+ /**
2648
+ * Groups related radio items.
2649
+ * Renders a `<div>` element.
2650
+ *
2651
+ * Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
2652
+ */
2653
+ const MenuRadioGroup = /*#__PURE__*/ React.memo(/*#__PURE__*/ React.forwardRef(function MenuRadioGroup(componentProps, forwardedRef) {
2654
+ const { render, className, value: valueProp, defaultValue, onValueChange: onValueChangeProp, disabled = false, style, "aria-labelledby": ariaLabelledByProp, ...elementProps } = componentProps;
2655
+ const [labelId, setLabelId] = React.useState(void 0);
2656
+ const [value, setValueUnwrapped] = useControlled({
2657
+ controlled: valueProp,
2658
+ default: defaultValue,
2659
+ name: "MenuRadioGroup"
2660
+ });
2661
+ const setValue = useStableCallback((newValue, eventDetails) => {
2662
+ onValueChangeProp?.(newValue, eventDetails);
2663
+ if (eventDetails.isCanceled) return;
2664
+ setValueUnwrapped(newValue);
2665
+ });
2666
+ const element = useRenderElement("div", componentProps, {
2667
+ state: { disabled },
2668
+ ref: forwardedRef,
2669
+ props: {
2670
+ role: "group",
2671
+ "aria-labelledby": ariaLabelledByProp ?? labelId,
2672
+ "aria-disabled": disabled || void 0,
2673
+ ...elementProps
2674
+ }
2675
+ });
2676
+ const context = React.useMemo(() => ({
2677
+ value,
2678
+ setValue,
2679
+ disabled
2680
+ }), [
2681
+ value,
2682
+ setValue,
2683
+ disabled
2684
+ ]);
2685
+ return /*#__PURE__*/ jsx(MenuGroupContext.Provider, {
2686
+ value: setLabelId,
2687
+ children: /*#__PURE__*/ jsx(MenuRadioGroupContext.Provider, {
2688
+ value: context,
2689
+ children: element
2690
+ })
2691
+ });
2692
+ }));
2693
+ MenuRadioGroup.displayName = "MenuRadioGroup";
2694
+
2695
+ //#endregion
2696
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/radio-item/MenuRadioItemContext.mjs
2697
+ const MenuRadioItemContext = /*#__PURE__*/ React.createContext(void 0);
2698
+ MenuRadioItemContext.displayName = "MenuRadioItemContext";
2699
+ function useMenuRadioItemContext() {
2700
+ const context = React.useContext(MenuRadioItemContext);
2701
+ if (context === void 0) throw new Error("Base UI: MenuRadioItemContext is missing. MenuRadioItem parts must be placed within <Menu.RadioItem>.");
2702
+ return context;
2703
+ }
2704
+
2705
+ //#endregion
2706
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/radio-item/MenuRadioItem.mjs
2707
+ /**
2708
+ * A menu item that works like a radio button in a given group.
2709
+ * Renders a `<div>` element.
2710
+ *
2711
+ * Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
2712
+ */
2713
+ const MenuRadioItem = /*#__PURE__*/ React.forwardRef(function MenuRadioItem(componentProps, forwardedRef) {
2714
+ const { render, className, id: idProp, label, nativeButton = false, disabled: disabledProp = false, closeOnClick = false, value, style, ...elementProps } = componentProps;
2715
+ const listItem = useCompositeListItem({
2716
+ guess: true,
2717
+ label
2718
+ });
2719
+ const menuPositionerContext = useMenuPositionerContext(true);
2720
+ const id = useBaseUiId(idProp);
2721
+ const { store } = useMenuRootContext();
2722
+ const highlighted = store.useState("isActive", listItem.index);
2723
+ const itemProps = store.useState("itemProps");
2724
+ const { value: selectedValue, setValue: setSelectedValue, disabled: groupDisabled } = useMenuRadioGroupContext();
2725
+ const rootDisabled = store.useState("disabled");
2726
+ const disabled = disabledProp || groupDisabled || rootDisabled;
2727
+ const checked = selectedValue === value;
2728
+ const { getItemProps, itemRef } = useMenuItem({
2729
+ closeOnClick,
2730
+ disabled,
2731
+ highlighted,
2732
+ id,
2733
+ store,
2734
+ nativeButton,
2735
+ nodeId: menuPositionerContext?.context.nodeId,
2736
+ itemMetadata: REGULAR_ITEM
2737
+ });
2738
+ const state = React.useMemo(() => ({
2739
+ disabled,
2740
+ highlighted,
2741
+ checked
2742
+ }), [
2743
+ disabled,
2744
+ highlighted,
2745
+ checked
2746
+ ]);
2747
+ function handleClick(event) {
2748
+ const details = createChangeEventDetails(itemPress, event.nativeEvent, void 0, { preventUnmountOnClose: NOOP });
2749
+ setSelectedValue(value, details);
2750
+ }
2751
+ const element = useRenderElement("div", componentProps, {
2752
+ state,
2753
+ stateAttributesMapping: itemMapping,
2754
+ props: [
2755
+ itemProps,
2756
+ {
2757
+ role: "menuitemradio",
2758
+ "aria-checked": checked,
2759
+ onClick: handleClick
2760
+ },
2761
+ elementProps,
2762
+ getItemProps
2763
+ ],
2764
+ ref: [
2765
+ itemRef,
2766
+ forwardedRef,
2767
+ listItem.ref
2768
+ ]
2769
+ });
2770
+ return /*#__PURE__*/ jsx(MenuRadioItemContext.Provider, {
2771
+ value: state,
2772
+ children: element
2773
+ });
2774
+ });
2775
+ MenuRadioItem.displayName = "MenuRadioItem";
2776
+
2777
+ //#endregion
2778
+ //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menu/radio-item-indicator/MenuRadioItemIndicator.mjs
2779
+ /**
2780
+ * Indicates whether the radio item is selected.
2781
+ * Renders a `<span>` element.
2782
+ *
2783
+ * Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
2784
+ */
2785
+ const MenuRadioItemIndicator = /*#__PURE__*/ React.forwardRef(function MenuRadioItemIndicator(componentProps, forwardedRef) {
2786
+ const { render, className, style, keepMounted = false, ...elementProps } = componentProps;
2787
+ const item = useMenuRadioItemContext();
2788
+ const indicatorRef = React.useRef(null);
2789
+ const { transitionStatus, mounted, setMounted } = useTransitionStatus(item.checked);
2790
+ useOpenChangeComplete({
2791
+ open: item.checked,
2792
+ ref: indicatorRef,
2793
+ onComplete() {
2794
+ if (!item.checked) setMounted(false);
2795
+ }
2796
+ });
2797
+ const state = {
2798
+ checked: item.checked,
2799
+ disabled: item.disabled,
2800
+ highlighted: item.highlighted,
2801
+ transitionStatus
2802
+ };
2803
+ return useRenderElement("span", componentProps, {
2804
+ state,
2805
+ stateAttributesMapping: itemMapping,
2806
+ ref: [forwardedRef, indicatorRef],
2807
+ props: {
2808
+ "aria-hidden": true,
2809
+ ...elementProps
2810
+ },
2811
+ enabled: keepMounted || mounted
2812
+ });
2813
+ });
2814
+ MenuRadioItemIndicator.displayName = "MenuRadioItemIndicator";
2815
+
2598
2816
  //#endregion
2599
2817
  //#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/menubar/MenubarContext.mjs
2600
2818
  const MenubarContext = /*#__PURE__*/ React.createContext(null);
@@ -3403,6 +3621,25 @@ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
3403
3621
  ...props
3404
3622
  });
3405
3623
  }
3624
+ function DropdownMenuRadioGroup({ ...props }) {
3625
+ return /* @__PURE__ */ jsx(MenuRadioGroup, {
3626
+ "data-slot": "dropdown-menu-radio-group",
3627
+ ...props
3628
+ });
3629
+ }
3630
+ function DropdownMenuRadioItem({ className, children, inset, ...props }) {
3631
+ return /* @__PURE__ */ jsxs(MenuRadioItem, {
3632
+ "data-slot": "dropdown-menu-radio-item",
3633
+ "data-inset": inset,
3634
+ className: cn("relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
3635
+ ...props,
3636
+ children: [/* @__PURE__ */ jsx("span", {
3637
+ className: "pointer-events-none absolute right-2 flex items-center justify-center",
3638
+ "data-slot": "dropdown-menu-radio-item-indicator",
3639
+ children: /* @__PURE__ */ jsx(MenuRadioItemIndicator, { children: /* @__PURE__ */ jsx(Check, {}) })
3640
+ }), children]
3641
+ });
3642
+ }
3406
3643
 
3407
3644
  //#endregion
3408
3645
  //#region src/lib/paths.ts
@@ -4208,6 +4445,15 @@ function normalizeNavbar(config) {
4208
4445
  primary
4209
4446
  } : null;
4210
4447
  }
4448
+ function normalizePlayground(config) {
4449
+ const playground = config?.playground;
4450
+ const display = playground?.display === "simple" || playground?.display === "none" ? playground.display : "interactive";
4451
+ const proxy = playground?.proxy?.trim() || void 0;
4452
+ return proxy ? {
4453
+ display,
4454
+ proxy
4455
+ } : { display };
4456
+ }
4211
4457
  function normalizeFooter(config) {
4212
4458
  const socials = (config?.socials || []).filter((link) => !!link?.href).map(normalizeLink);
4213
4459
  const links = (config?.links || []).filter((column) => column?.items?.length).map((column) => ({
@@ -4265,6 +4511,7 @@ function resolveSiteModel(config, docs, shiso) {
4265
4511
  drilldown: config.interaction?.drilldown,
4266
4512
  locale: shiso?.locale || "en-US",
4267
4513
  labels: resolveLabels(shiso?.locale || "en-US", shiso?.translations),
4514
+ api: { playground: normalizePlayground(config.api) },
4268
4515
  docs
4269
4516
  };
4270
4517
  }
@@ -4538,27 +4785,29 @@ function LanguageSwitcher() {
4538
4785
  const standalone = getStandalonePage(pathname);
4539
4786
  if (!current.language || options.length < 2 && !current.hidden) return null;
4540
4787
  return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
4541
- render: /* @__PURE__ */ jsx(Button, {
4542
- variant: "outline",
4543
- className: "h-auto gap-1.5 rounded-md bg-card px-2.5 py-1.5 text-sm font-medium text-foreground"
4544
- }),
4788
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline" }),
4789
+ "aria-label": `Language: ${getLanguageName(current.language)}`,
4545
4790
  children: [getLanguageName(current.language), /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 rotate-90 text-muted-foreground" })]
4546
4791
  }), /* @__PURE__ */ jsx(DropdownMenuContent, {
4547
4792
  align: "start",
4548
4793
  className: "min-w-32",
4549
- children: options.map((scope) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
4550
- onClick: () => {
4551
- if (scope.language !== current.language) {
4794
+ children: /* @__PURE__ */ jsx(DropdownMenuRadioGroup, {
4795
+ value: current.language,
4796
+ onValueChange: (language) => {
4797
+ const scope = options.find((option) => option.language === language);
4798
+ if (scope && scope.language !== current.language) {
4552
4799
  const counterpart = standalone && scope.language ? getStandaloneCounterpart(standalone, scope.language) : null;
4553
4800
  navigate(counterpart?.path || scope.firstPageUrl);
4554
4801
  }
4555
4802
  },
4556
- children: [/* @__PURE__ */ jsx("span", {
4557
- className: "grow",
4558
- lang: isValidLocale(scope.language) ? scope.language : void 0,
4559
- children: scope.language ? getLanguageName(scope.language) : null
4560
- }), scope.language === current.language ? /* @__PURE__ */ jsx(Check, { className: "size-3.5" }) : null]
4561
- }, scope.id))
4803
+ children: options.map((scope) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
4804
+ value: scope.language,
4805
+ children: /* @__PURE__ */ jsx("span", {
4806
+ lang: isValidLocale(scope.language) ? scope.language : void 0,
4807
+ children: scope.language ? getLanguageName(scope.language) : null
4808
+ })
4809
+ }, scope.id))
4810
+ })
4562
4811
  })] });
4563
4812
  }
4564
4813
 
@@ -8078,8 +8327,39 @@ const METHOD_COLORS = {
8078
8327
  POST: "blue",
8079
8328
  PUT: "orange",
8080
8329
  PATCH: "purple",
8081
- DELETE: "red"
8330
+ DELETE: "red",
8331
+ WEBHOOK: "yellow"
8082
8332
  };
8333
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
8334
+ "GET",
8335
+ "POST",
8336
+ "PUT",
8337
+ "PATCH",
8338
+ "DELETE",
8339
+ "HEAD",
8340
+ "OPTIONS",
8341
+ "TRACE"
8342
+ ]);
8343
+ /** Short sidebar badge text for a method. */
8344
+ function methodBadgeText(method) {
8345
+ if (method === "DELETE") return "DEL";
8346
+ if (method === "WEBHOOK") return "HOOK";
8347
+ return method;
8348
+ }
8349
+ /**
8350
+ * Normalizes an `openapi:` frontmatter value into a lookup key. Mirrors
8351
+ * normalizeOperationKey in scripts/lib/openapi.mjs: the method (or "webhook")
8352
+ * is uppercased and an optional leading spec qualifier is kept verbatim.
8353
+ */
8354
+ function normalizeOperationKey(value) {
8355
+ if (typeof value !== "string" || !value.trim()) return void 0;
8356
+ const [first, ...rest] = value.trim().split(/\s+/);
8357
+ if (!rest.length) return void 0;
8358
+ const upper = first.toUpperCase();
8359
+ if (upper === "WEBHOOK" || HTTP_METHODS.has(upper)) return `${upper} ${rest.join(" ")}`;
8360
+ const inner = normalizeOperationKey(rest.join(" "));
8361
+ return inner ? `${first} ${inner}` : void 0;
8362
+ }
8083
8363
  function methodColor(method) {
8084
8364
  return method && METHOD_COLORS[method.toUpperCase()] || "gray";
8085
8365
  }
@@ -8089,11 +8369,51 @@ function statusColor(status) {
8089
8369
  if (status.startsWith("4") || status.startsWith("5")) return "red";
8090
8370
  return "gray";
8091
8371
  }
8372
+ /**
8373
+ * Resolves the effective playground display for a page: the frontmatter
8374
+ * `playground` value wins over `api.playground.display`.
8375
+ */
8376
+ function resolvePlaygroundDisplay(playground, frontmatter) {
8377
+ const override = frontmatter?.playground;
8378
+ if (override === "interactive" || override === "simple" || override === "none") return override;
8379
+ return playground.display;
8380
+ }
8092
8381
  /** Looks up the operation bound by an `openapi:` frontmatter value. */
8093
8382
  function getOperation(key) {
8094
- if (typeof key !== "string" || !key.trim()) return;
8095
- const [method, ...rest] = key.trim().split(/\s+/);
8096
- return OPENAPI_OPERATIONS[`${method.toUpperCase()} ${rest.join(" ")}`];
8383
+ const normalized = normalizeOperationKey(key);
8384
+ return normalized ? OPENAPI_OPERATIONS[normalized] : void 0;
8385
+ }
8386
+ /** Looks up the schema bound by an `openapi-schema:` frontmatter value. */
8387
+ function getSchema(key) {
8388
+ if (typeof key !== "string" || !key.trim()) return void 0;
8389
+ return OPENAPI_SCHEMAS[key.trim().split(/\s+/).join(" ")];
8390
+ }
8391
+ /**
8392
+ * Section headings for a schema page, in render order. Mirrors schemaAnchors
8393
+ * in scripts/lib/openapi.mjs.
8394
+ */
8395
+ function schemaSections(page, labels = englishLabels) {
8396
+ return [...page.schema.children?.length ? [{
8397
+ name: labels.apiSchemaProperties,
8398
+ id: "properties",
8399
+ size: 2
8400
+ }] : [], ...page.example ? [{
8401
+ name: labels.apiExample,
8402
+ id: "example",
8403
+ size: 2
8404
+ }] : []];
8405
+ }
8406
+ /** Where a security scheme's credential travels: a header unless it is a query or cookie API key. */
8407
+ function securityLocation(scheme) {
8408
+ return scheme.type === "apiKey" && scheme.in ? scheme.in : "header";
8409
+ }
8410
+ /** Security schemes that are sent in the given parameter location. */
8411
+ function securityForLocation(operation, location) {
8412
+ return operation.security.filter((scheme) => securityLocation(scheme) === location);
8413
+ }
8414
+ /** Display name of the header, query, or cookie entry a scheme adds. */
8415
+ function securityFieldName(scheme) {
8416
+ return scheme.type === "apiKey" ? scheme.paramName || scheme.name : "Authorization";
8097
8417
  }
8098
8418
  /** Parameter groups shared by the renderer and table of contents. */
8099
8419
  function operationParameterSections(operation, labels = englishLabels) {
@@ -8114,24 +8434,27 @@ function operationParameterSections(operation, labels = englishLabels) {
8114
8434
  location: "cookie",
8115
8435
  name: labels.apiCookieParameters
8116
8436
  }
8117
- ].filter(({ location }) => operation.parameters[location].length > 0 || location === "header" && operation.security.length > 0);
8437
+ ].filter(({ location }) => operation.parameters[location].length > 0 || securityForLocation(operation, location).length > 0);
8118
8438
  }
8119
8439
  /**
8120
8440
  * Section headings for an operation, in render order. The single source of
8121
8441
  * truth for section ids: the component, the table of contents, the content
8122
8442
  * checker, and the search indexer all derive their anchors from these labels.
8123
8443
  */
8124
- function operationSections(operation, labels = englishLabels) {
8444
+ function operationSections(operation, labels = englishLabels, options = {}) {
8125
8445
  const slugger = createSlugger();
8446
+ const playground = options.playground && !operation.webhook;
8126
8447
  const names = [
8448
+ playground ? "Try it" : void 0,
8127
8449
  ...operationParameterSections(operation).map((section) => section.name),
8128
- operation.requestBody ? "Request body" : void 0,
8450
+ operation.requestBody ? operation.webhook ? "Payload" : "Request body" : void 0,
8129
8451
  operation.responses.length ? "Responses" : void 0,
8130
8452
  operation.samples.length ? "Code samples" : void 0
8131
8453
  ].filter((name) => Boolean(name));
8132
8454
  const translated = [
8455
+ ...playground ? [labels.apiPlayground] : [],
8133
8456
  ...operationParameterSections(operation, labels).map((section) => section.name),
8134
- ...operation.requestBody ? [labels.apiRequestBody] : [],
8457
+ ...operation.requestBody ? [operation.webhook ? labels.apiPayload : labels.apiRequestBody] : [],
8135
8458
  ...operation.responses.length ? [labels.apiResponses] : [],
8136
8459
  ...operation.samples.length ? [labels.apiCodeSamples] : []
8137
8460
  ];
@@ -8142,9 +8465,635 @@ function operationSections(operation, labels = englishLabels) {
8142
8465
  }));
8143
8466
  }
8144
8467
 
8468
+ //#endregion
8469
+ //#region scripts/lib/request-samples.mjs
8470
+ /**
8471
+ * Request building shared by the build-time sample generator and the browser
8472
+ * API playground. This module is deliberately free of Node imports: it is
8473
+ * bundled into the client runtime as well as executed by the build scripts.
8474
+ *
8475
+ * `buildRequest` turns an operation plus user-supplied values into a concrete
8476
+ * HTTP request description; `buildCodeSamples` renders that description as
8477
+ * cURL, JavaScript, and Python. Values that are missing fall back to the
8478
+ * spec's examples or to angle-bracket placeholders such as `<token>`, so the
8479
+ * static samples and the live playground always agree on the request shape.
8480
+ */
8481
+ const PLACEHOLDER = /^<[^<>]+>$/;
8482
+ /** Percent-encodes a value unless it is a documentation placeholder. */
8483
+ function encodeValue(value) {
8484
+ return PLACEHOLDER.test(value) ? value : encodeURIComponent(value);
8485
+ }
8486
+ function toBase64(value) {
8487
+ if (typeof btoa === "function") return btoa(value);
8488
+ return Buffer.from(value, "utf8").toString("base64");
8489
+ }
8490
+ function capitalize(value) {
8491
+ return value.charAt(0).toUpperCase() + value.slice(1);
8492
+ }
8493
+ /** Placeholder credential text shown in samples when no value is supplied. */
8494
+ function securityPlaceholder(scheme) {
8495
+ if (scheme.type === "http") return scheme.scheme === "basic" ? "<credentials>" : "<token>";
8496
+ if (scheme.type === "apiKey") return "<api-key>";
8497
+ return "<access-token>";
8498
+ }
8499
+ /**
8500
+ * Applies one security scheme to the request. `value` is a string for token
8501
+ * and API key schemes, `{ username, password }` for HTTP basic, or undefined
8502
+ * to emit a placeholder.
8503
+ */
8504
+ function applySecurity(scheme, value, target) {
8505
+ const placeholder = securityPlaceholder(scheme);
8506
+ if (scheme.type === "apiKey") {
8507
+ const credential = typeof value === "string" && value ? value : placeholder;
8508
+ const name = scheme.paramName || scheme.name;
8509
+ if (scheme.in === "query") target.query[name] = credential;
8510
+ else if (scheme.in === "cookie") target.cookies[name] = credential;
8511
+ else target.headers[name] = credential;
8512
+ return;
8513
+ }
8514
+ if (scheme.type === "http" && scheme.scheme === "basic") {
8515
+ const credential = value && typeof value === "object" && (value.username || value.password) ? toBase64(`${value.username || ""}:${value.password || ""}`) : typeof value === "string" && value ? value : placeholder;
8516
+ target.headers.Authorization = `Basic ${credential}`;
8517
+ return;
8518
+ }
8519
+ const prefix = scheme.type === "http" && scheme.scheme && scheme.scheme !== "bearer" ? capitalize(scheme.scheme) : "Bearer";
8520
+ const credential = typeof value === "string" && value ? value : placeholder;
8521
+ target.headers.Authorization = `${prefix} ${credential}`;
8522
+ }
8523
+ /** Content categories the sample renderers and playground understand. */
8524
+ function bodyKind(contentType) {
8525
+ if (!contentType) return "none";
8526
+ if (/[+/]json\b/i.test(contentType)) return "json";
8527
+ if (/x-www-form-urlencoded/i.test(contentType)) return "form";
8528
+ if (/multipart\/form-data/i.test(contentType)) return "multipart";
8529
+ return "raw";
8530
+ }
8531
+ function parseFields(body) {
8532
+ if (!body) return {};
8533
+ try {
8534
+ const parsed = JSON.parse(body);
8535
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8536
+ return Object.fromEntries(Object.entries(parsed).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)]));
8537
+ } catch {
8538
+ return {};
8539
+ }
8540
+ }
8541
+ function parameterValue(node, supplied) {
8542
+ if (supplied !== void 0 && supplied !== null && supplied !== "") return String(supplied);
8543
+ return node.example;
8544
+ }
8545
+ /**
8546
+ * Builds a concrete request from an operation and optional user values:
8547
+ * `{ server, path, query, header, cookie, auth, body }`. Without values, the
8548
+ * spec's examples fill required parameters and credentials become
8549
+ * placeholders, which is the shape rendered in the static code samples.
8550
+ */
8551
+ function buildRequest(operation, values = {}) {
8552
+ const live = values.live === true;
8553
+ const target = {
8554
+ headers: {},
8555
+ query: {},
8556
+ cookies: {}
8557
+ };
8558
+ for (const scheme of operation.security || []) applySecurity(scheme, values.auth?.[scheme.name], target);
8559
+ const params = operation.parameters;
8560
+ const pathValues = {};
8561
+ for (const node of params.path) {
8562
+ const value = parameterValue(node, values.path?.[node.name]);
8563
+ if (live && value !== void 0) pathValues[node.name] = value;
8564
+ }
8565
+ for (const node of params.query) {
8566
+ const value = parameterValue(node, values.query?.[node.name]);
8567
+ if (value !== void 0 && (live || node.required || values.query?.[node.name])) target.query[node.name] = value;
8568
+ }
8569
+ for (const node of params.header) {
8570
+ const value = parameterValue(node, values.header?.[node.name]);
8571
+ if (value !== void 0 && (live || node.required || values.header?.[node.name])) target.headers[node.name] = value;
8572
+ }
8573
+ for (const node of params.cookie) {
8574
+ const value = parameterValue(node, values.cookie?.[node.name]);
8575
+ if (value !== void 0 && (live || node.required || values.cookie?.[node.name])) target.cookies[node.name] = value;
8576
+ }
8577
+ const server = (values.server || operation.servers?.[0]?.url || operation.serverUrl || "").trim().replace(/\/+$/, "");
8578
+ const pathname = operation.path.replace(/\{([^}]+)\}/g, (match, name) => pathValues[name] !== void 0 ? encodeURIComponent(pathValues[name]) : match);
8579
+ const query = Object.entries(target.query).map(([key, value]) => `${encodeURIComponent(key)}=${encodeValue(value)}`).join("&");
8580
+ const url = `${server}${pathname}${query ? `?${query}` : ""}`;
8581
+ const cookieHeader = Object.entries(target.cookies).map(([key, value]) => `${key}=${value}`).join("; ");
8582
+ if (cookieHeader) target.headers.Cookie = cookieHeader;
8583
+ const contentType = operation.requestBody?.contentType;
8584
+ const kind = operation.requestBody ? bodyKind(contentType) : "none";
8585
+ const body = values.body !== void 0 ? values.body : operation.requestBody?.example;
8586
+ return {
8587
+ method: operation.method,
8588
+ url,
8589
+ headers: target.headers,
8590
+ contentType,
8591
+ bodyKind: kind,
8592
+ body: kind === "none" ? void 0 : body,
8593
+ fields: kind === "form" || kind === "multipart" ? parseFields(body) : void 0
8594
+ };
8595
+ }
8596
+ function shellQuote(value) {
8597
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
8598
+ }
8599
+ function curlSample(request) {
8600
+ const { method, url, headers, bodyKind: kind, body, fields, contentType } = request;
8601
+ const lines = [`curl -X ${method} ${shellQuote(url)}`];
8602
+ for (const [key, value] of Object.entries(headers)) lines.push(` -H ${shellQuote(`${key}: ${value}`)}`);
8603
+ if (kind === "json" && body) lines.push(` -H ${shellQuote(`Content-Type: ${contentType}`)}`, ` -d ${shellQuote(body)}`);
8604
+ else if (kind === "form") for (const [key, value] of Object.entries(fields || {})) lines.push(` --data-urlencode ${shellQuote(`${key}=${value}`)}`);
8605
+ else if (kind === "multipart") for (const [key, value] of Object.entries(fields || {})) lines.push(` -F ${shellQuote(`${key}=${value}`)}`);
8606
+ else if (kind === "raw" && body) lines.push(` -H ${shellQuote(`Content-Type: ${contentType}`)}`, ` -d ${shellQuote(body)}`);
8607
+ return lines.join(" \\\n");
8608
+ }
8609
+
8610
+ //#endregion
8611
+ //#region src/components/ApiPlayground.tsx
8612
+ const REQUEST_TIMEOUT_MS = 3e4;
8613
+ const AUTH_STORAGE_KEY = "shiso:api-auth";
8614
+ const BODYLESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
8615
+ const PARAMETER_LOCATIONS = [
8616
+ "path",
8617
+ "query",
8618
+ "header"
8619
+ ];
8620
+ const inputClass = "h-8 w-full min-w-0 rounded-md border border-border bg-background px-2.5 font-mono text-foreground text-sm placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/50";
8621
+ function isBasic(scheme) {
8622
+ return scheme.type === "http" && scheme.scheme === "basic";
8623
+ }
8624
+ function initialParameters(operation) {
8625
+ const values = {
8626
+ path: {},
8627
+ query: {},
8628
+ header: {}
8629
+ };
8630
+ for (const location of PARAMETER_LOCATIONS) for (const node of operation.parameters[location]) if (node.name) values[location][node.name] = node.example ?? "";
8631
+ return values;
8632
+ }
8633
+ function readStoredAuth() {
8634
+ try {
8635
+ const raw = window.sessionStorage.getItem(AUTH_STORAGE_KEY);
8636
+ const parsed = raw ? JSON.parse(raw) : void 0;
8637
+ return parsed && typeof parsed === "object" ? parsed : {};
8638
+ } catch {
8639
+ return {};
8640
+ }
8641
+ }
8642
+ function writeStoredAuth(values) {
8643
+ try {
8644
+ window.sessionStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(values));
8645
+ } catch {}
8646
+ }
8647
+ /** Resolves the playground proxy template into the URL to fetch. */
8648
+ function proxiedUrl(url, proxy) {
8649
+ if (!proxy) return url;
8650
+ const encoded = encodeURIComponent(url);
8651
+ return proxy.includes("$url") ? proxy.replaceAll("$url", encoded) : `${proxy}${encoded}`;
8652
+ }
8653
+ function prettyBody(text, contentType) {
8654
+ if (/json/i.test(contentType || "") || /^\s*[{[]/.test(text)) try {
8655
+ return {
8656
+ body: JSON.stringify(JSON.parse(text), null, 2),
8657
+ json: true
8658
+ };
8659
+ } catch {}
8660
+ return {
8661
+ body: text,
8662
+ json: false
8663
+ };
8664
+ }
8665
+ function ParameterInput({ id, node, value, onChange }) {
8666
+ const options = node.enum ?? (node.type === "boolean" ? ["true", "false"] : void 0);
8667
+ if (options) return /* @__PURE__ */ jsxs("select", {
8668
+ id,
8669
+ className: inputClass,
8670
+ value,
8671
+ required: node.required,
8672
+ onChange: (event) => onChange(event.target.value),
8673
+ children: [!node.required && /* @__PURE__ */ jsx("option", {
8674
+ value: "",
8675
+ children: "—"
8676
+ }), options.map((option) => /* @__PURE__ */ jsx("option", {
8677
+ value: option,
8678
+ children: option
8679
+ }, option))]
8680
+ });
8681
+ return /* @__PURE__ */ jsx("input", {
8682
+ id,
8683
+ type: "text",
8684
+ className: inputClass,
8685
+ value,
8686
+ required: node.required,
8687
+ placeholder: node.type,
8688
+ autoComplete: "off",
8689
+ spellCheck: false,
8690
+ onChange: (event) => onChange(event.target.value)
8691
+ });
8692
+ }
8693
+ function Field({ id, label, required, hint, children }) {
8694
+ const labels = useLabels();
8695
+ return /* @__PURE__ */ jsxs("div", {
8696
+ className: "grid grid-cols-[minmax(0,1fr)] gap-1 sm:grid-cols-[minmax(8rem,30%)_minmax(0,1fr)] sm:items-center sm:gap-3",
8697
+ children: [/* @__PURE__ */ jsxs("label", {
8698
+ htmlFor: id,
8699
+ className: "flex flex-wrap items-baseline gap-1.5 text-sm",
8700
+ children: [/* @__PURE__ */ jsx("span", {
8701
+ className: "font-mono text-foreground",
8702
+ children: label
8703
+ }), required ? /* @__PURE__ */ jsx("span", {
8704
+ className: "text-destructive text-xs",
8705
+ children: labels.fieldRequired
8706
+ }) : hint ? /* @__PURE__ */ jsx("span", {
8707
+ className: "text-muted-foreground text-xs",
8708
+ children: hint
8709
+ }) : null]
8710
+ }), children]
8711
+ });
8712
+ }
8713
+ /**
8714
+ * The "Try it" panel: a form built from the operation's servers, security
8715
+ * schemes, parameters, and request body that sends real requests from the
8716
+ * browser and shows the response. Credentials persist in sessionStorage so a
8717
+ * token entered once is reused on every endpoint page during the tab's life.
8718
+ */
8719
+ function ApiPlayground({ operation, proxy }) {
8720
+ const labels = useLabels();
8721
+ const idPrefix = useId();
8722
+ const [server, setServer] = useState(operation.servers[0]?.url ?? operation.serverUrl);
8723
+ const [auth, setAuth] = useState({});
8724
+ const [parameters, setParameters] = useState(() => initialParameters(operation));
8725
+ const [body, setBody] = useState(operation.requestBody?.example ?? "");
8726
+ const [status, setStatus] = useState("idle");
8727
+ const [response, setResponse] = useState();
8728
+ const [error, setError] = useState();
8729
+ const controller = useRef(null);
8730
+ useEffect(() => {
8731
+ setAuth(readStoredAuth());
8732
+ }, []);
8733
+ useEffect(() => () => controller.current?.abort(), []);
8734
+ const hasBody = !!operation.requestBody && !BODYLESS_METHODS.has(operation.method);
8735
+ const requestValues = useMemo(() => ({
8736
+ live: true,
8737
+ server,
8738
+ auth,
8739
+ body: hasBody ? body : void 0,
8740
+ ...parameters
8741
+ }), [
8742
+ server,
8743
+ auth,
8744
+ body,
8745
+ hasBody,
8746
+ parameters
8747
+ ]);
8748
+ const preview = useMemo(() => curlSample(buildRequest(operation, requestValues)), [operation, requestValues]);
8749
+ function updateAuth(name, value) {
8750
+ setAuth((current) => {
8751
+ const next = {
8752
+ ...current,
8753
+ [name]: value
8754
+ };
8755
+ writeStoredAuth(next);
8756
+ return next;
8757
+ });
8758
+ }
8759
+ function updateParameter(location, name, value) {
8760
+ setParameters((current) => ({
8761
+ ...current,
8762
+ [location]: {
8763
+ ...current[location],
8764
+ [name]: value
8765
+ }
8766
+ }));
8767
+ }
8768
+ function cancel() {
8769
+ controller.current?.abort();
8770
+ }
8771
+ async function send(event) {
8772
+ event.preventDefault();
8773
+ if (status === "pending") return;
8774
+ const request = buildRequest(operation, requestValues);
8775
+ const abort = new AbortController();
8776
+ controller.current = abort;
8777
+ setStatus("pending");
8778
+ setError(void 0);
8779
+ const timeout = window.setTimeout(() => abort.abort("timeout"), REQUEST_TIMEOUT_MS);
8780
+ const started = performance.now();
8781
+ try {
8782
+ const headers = new Headers();
8783
+ for (const [key, value] of Object.entries(request.headers)) if (key.toLowerCase() !== "cookie") headers.set(key, value);
8784
+ let payload;
8785
+ if (hasBody) {
8786
+ const fields = request.fields ?? {};
8787
+ if (request.bodyKind === "form") payload = new URLSearchParams(fields);
8788
+ else if (request.bodyKind === "multipart") {
8789
+ const form = new FormData();
8790
+ for (const [key, value] of Object.entries(fields)) form.append(key, value);
8791
+ payload = form;
8792
+ } else if (request.body) {
8793
+ payload = request.body;
8794
+ if (request.contentType) headers.set("Content-Type", request.contentType);
8795
+ }
8796
+ }
8797
+ const result = await fetch(proxiedUrl(request.url, proxy), {
8798
+ method: request.method,
8799
+ headers,
8800
+ body: payload,
8801
+ signal: abort.signal
8802
+ });
8803
+ const { body: pretty, json } = prettyBody(await result.text(), result.headers.get("content-type"));
8804
+ const responseHeaders = [];
8805
+ result.headers.forEach((value, key) => {
8806
+ responseHeaders.push([key, value]);
8807
+ });
8808
+ setResponse({
8809
+ status: result.status,
8810
+ statusText: result.statusText,
8811
+ headers: responseHeaders,
8812
+ body: pretty,
8813
+ json,
8814
+ elapsed: Math.round(performance.now() - started)
8815
+ });
8816
+ setStatus("done");
8817
+ } catch (caught) {
8818
+ if (abort.signal.aborted && abort.signal.reason === "timeout") {
8819
+ setError(labels.apiRequestTimedOut);
8820
+ setStatus("error");
8821
+ } else if (abort.signal.aborted) setStatus(response ? "done" : "idle");
8822
+ else {
8823
+ const detail = caught instanceof Error && caught.message ? ` (${caught.message})` : "";
8824
+ setError(`${labels.apiRequestFailed}${detail}`);
8825
+ setStatus("error");
8826
+ }
8827
+ } finally {
8828
+ window.clearTimeout(timeout);
8829
+ if (controller.current === abort) controller.current = null;
8830
+ }
8831
+ }
8832
+ const serverListId = `${idPrefix}-servers`;
8833
+ const parameterGroups = PARAMETER_LOCATIONS.map((location) => ({
8834
+ location,
8835
+ name: {
8836
+ path: labels.apiPathParameters,
8837
+ query: labels.apiQueryParameters,
8838
+ header: labels.apiHeaders
8839
+ }[location],
8840
+ nodes: operation.parameters[location].filter((node) => node.name)
8841
+ })).filter((group) => group.nodes.length > 0);
8842
+ return /* @__PURE__ */ jsxs("form", {
8843
+ onSubmit: send,
8844
+ className: "not-prose my-4 flex flex-col gap-5 rounded-lg border border-border bg-card p-4 text-sm",
8845
+ "data-slot": "api-playground",
8846
+ "data-pagefind-ignore": true,
8847
+ "aria-busy": status === "pending",
8848
+ children: [
8849
+ /* @__PURE__ */ jsxs("div", {
8850
+ className: "flex flex-col gap-2",
8851
+ children: [/* @__PURE__ */ jsx("label", {
8852
+ htmlFor: `${idPrefix}-server`,
8853
+ className: "text-muted-foreground text-xs",
8854
+ children: labels.apiServer
8855
+ }), /* @__PURE__ */ jsxs("div", {
8856
+ className: "flex flex-wrap items-center gap-2",
8857
+ children: [
8858
+ /* @__PURE__ */ jsx(Badge, {
8859
+ color: methodColor(operation.method),
8860
+ size: "sm",
8861
+ className: "font-mono",
8862
+ children: operation.method
8863
+ }),
8864
+ /* @__PURE__ */ jsx("input", {
8865
+ id: `${idPrefix}-server`,
8866
+ type: "text",
8867
+ className: cn(inputClass, "flex-1 basis-48"),
8868
+ value: server,
8869
+ list: operation.servers.length > 1 ? serverListId : void 0,
8870
+ autoComplete: "off",
8871
+ spellCheck: false,
8872
+ onChange: (event) => setServer(event.target.value)
8873
+ }),
8874
+ operation.servers.length > 1 && /* @__PURE__ */ jsx("datalist", {
8875
+ id: serverListId,
8876
+ children: operation.servers.map((item) => /* @__PURE__ */ jsx("option", {
8877
+ value: item.url,
8878
+ children: item.description
8879
+ }, item.url))
8880
+ }),
8881
+ /* @__PURE__ */ jsx("code", {
8882
+ className: "font-mono text-muted-foreground",
8883
+ children: operation.path
8884
+ })
8885
+ ]
8886
+ })]
8887
+ }),
8888
+ operation.security.length > 0 && /* @__PURE__ */ jsxs("fieldset", {
8889
+ className: "flex flex-col gap-3",
8890
+ children: [
8891
+ /* @__PURE__ */ jsx("legend", {
8892
+ className: "mb-2 text-muted-foreground text-xs",
8893
+ children: labels.apiAuthorization
8894
+ }),
8895
+ operation.security.map((scheme) => {
8896
+ const value = auth[scheme.name];
8897
+ const fieldId = `${idPrefix}-auth-${scheme.name}`;
8898
+ if (isBasic(scheme)) {
8899
+ const basic = value && typeof value === "object" ? value : {
8900
+ username: "",
8901
+ password: ""
8902
+ };
8903
+ return /* @__PURE__ */ jsxs("div", {
8904
+ className: "flex flex-col gap-2",
8905
+ children: [/* @__PURE__ */ jsx(Field, {
8906
+ id: `${fieldId}-user`,
8907
+ label: labels.apiUsername,
8908
+ hint: scheme.name,
8909
+ children: /* @__PURE__ */ jsx("input", {
8910
+ id: `${fieldId}-user`,
8911
+ type: "text",
8912
+ className: inputClass,
8913
+ value: basic.username,
8914
+ autoComplete: "off",
8915
+ onChange: (event) => updateAuth(scheme.name, {
8916
+ ...basic,
8917
+ username: event.target.value
8918
+ })
8919
+ })
8920
+ }), /* @__PURE__ */ jsx(Field, {
8921
+ id: `${fieldId}-pass`,
8922
+ label: labels.apiPassword,
8923
+ children: /* @__PURE__ */ jsx("input", {
8924
+ id: `${fieldId}-pass`,
8925
+ type: "password",
8926
+ className: inputClass,
8927
+ value: basic.password,
8928
+ autoComplete: "off",
8929
+ onChange: (event) => updateAuth(scheme.name, {
8930
+ ...basic,
8931
+ password: event.target.value
8932
+ })
8933
+ })
8934
+ })]
8935
+ }, scheme.name);
8936
+ }
8937
+ const label = scheme.type === "apiKey" ? scheme.paramName || labels.apiApiKey : scheme.type === "http" && scheme.scheme !== "bearer" ? "Authorization" : labels.apiToken;
8938
+ return /* @__PURE__ */ jsx(Field, {
8939
+ id: fieldId,
8940
+ label,
8941
+ hint: scheme.label,
8942
+ children: /* @__PURE__ */ jsx("input", {
8943
+ id: fieldId,
8944
+ type: "password",
8945
+ className: inputClass,
8946
+ value: typeof value === "string" ? value : "",
8947
+ autoComplete: "off",
8948
+ onChange: (event) => updateAuth(scheme.name, event.target.value)
8949
+ })
8950
+ }, scheme.name);
8951
+ }),
8952
+ /* @__PURE__ */ jsx("p", {
8953
+ className: "m-0 text-muted-foreground text-xs",
8954
+ children: labels.apiCredentialsStored
8955
+ })
8956
+ ]
8957
+ }),
8958
+ parameterGroups.map((group) => /* @__PURE__ */ jsxs("fieldset", {
8959
+ className: "flex flex-col gap-3",
8960
+ children: [/* @__PURE__ */ jsx("legend", {
8961
+ className: "mb-2 text-muted-foreground text-xs",
8962
+ children: group.name
8963
+ }), group.nodes.map((node) => {
8964
+ const name = node.name;
8965
+ const fieldId = `${idPrefix}-${group.location}-${name}`;
8966
+ return /* @__PURE__ */ jsx(Field, {
8967
+ id: fieldId,
8968
+ label: name,
8969
+ required: node.required,
8970
+ hint: labels.apiOptional,
8971
+ children: /* @__PURE__ */ jsx(ParameterInput, {
8972
+ id: fieldId,
8973
+ node,
8974
+ value: parameters[group.location][name] ?? "",
8975
+ onChange: (value) => updateParameter(group.location, name, value)
8976
+ })
8977
+ }, name);
8978
+ })]
8979
+ }, group.location)),
8980
+ operation.parameters.cookie.length > 0 && /* @__PURE__ */ jsx("p", {
8981
+ className: "m-0 text-muted-foreground text-xs",
8982
+ children: labels.apiCookiesUnsupported
8983
+ }),
8984
+ hasBody && /* @__PURE__ */ jsxs("div", {
8985
+ className: "flex flex-col gap-2",
8986
+ children: [/* @__PURE__ */ jsxs("label", {
8987
+ htmlFor: `${idPrefix}-body`,
8988
+ className: "flex items-baseline gap-2 text-muted-foreground text-xs",
8989
+ children: [labels.apiBody, /* @__PURE__ */ jsx("span", {
8990
+ className: "font-mono",
8991
+ children: operation.requestBody?.contentType
8992
+ })]
8993
+ }), /* @__PURE__ */ jsx("textarea", {
8994
+ id: `${idPrefix}-body`,
8995
+ className: cn(inputClass, "h-auto min-h-32 resize-y py-2 leading-relaxed"),
8996
+ value: body,
8997
+ spellCheck: false,
8998
+ onChange: (event) => setBody(event.target.value)
8999
+ })]
9000
+ }),
9001
+ /* @__PURE__ */ jsxs("div", {
9002
+ className: "flex flex-wrap items-center gap-2",
9003
+ children: [/* @__PURE__ */ jsx(Button, {
9004
+ type: "submit",
9005
+ disabled: status === "pending",
9006
+ children: status === "pending" ? labels.apiSending : labels.apiSend
9007
+ }), status === "pending" && /* @__PURE__ */ jsx(Button, {
9008
+ type: "button",
9009
+ variant: "outline",
9010
+ onClick: cancel,
9011
+ children: labels.apiCancel
9012
+ })]
9013
+ }),
9014
+ /* @__PURE__ */ jsx(Expandable, {
9015
+ title: labels.apiRequest,
9016
+ children: /* @__PURE__ */ jsx(CodeBlock, {
9017
+ "data-language": "bash",
9018
+ "data-line-count": String(preview.split("\n").length),
9019
+ className: "text-xs",
9020
+ children: /* @__PURE__ */ jsx("code", {
9021
+ className: "language-bash",
9022
+ children: preview
9023
+ })
9024
+ })
9025
+ }),
9026
+ /* @__PURE__ */ jsxs("div", {
9027
+ className: "flex flex-col gap-2",
9028
+ role: "status",
9029
+ "aria-live": "polite",
9030
+ children: [
9031
+ /* @__PURE__ */ jsx("div", {
9032
+ className: "text-muted-foreground text-xs",
9033
+ children: labels.apiResponse
9034
+ }),
9035
+ status === "error" && error && /* @__PURE__ */ jsx("p", {
9036
+ className: "m-0 text-destructive",
9037
+ children: error
9038
+ }),
9039
+ response ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
9040
+ /* @__PURE__ */ jsxs("div", {
9041
+ className: "flex flex-wrap items-center gap-2",
9042
+ children: [
9043
+ /* @__PURE__ */ jsx(Badge, {
9044
+ color: statusColor(String(response.status)),
9045
+ size: "sm",
9046
+ children: response.status
9047
+ }),
9048
+ response.statusText && /* @__PURE__ */ jsx("span", {
9049
+ className: "text-muted-foreground",
9050
+ children: response.statusText
9051
+ }),
9052
+ /* @__PURE__ */ jsx("span", {
9053
+ className: "text-muted-foreground text-xs",
9054
+ children: labels.apiElapsed.replace("{ms}", () => String(response.elapsed))
9055
+ })
9056
+ ]
9057
+ }),
9058
+ response.headers.length > 0 && /* @__PURE__ */ jsx(Expandable, {
9059
+ title: labels.apiResponseHeaders,
9060
+ children: /* @__PURE__ */ jsx("dl", {
9061
+ className: "m-0 grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 font-mono text-xs",
9062
+ children: response.headers.map(([key, value]) => /* @__PURE__ */ jsxs("div", {
9063
+ className: "contents",
9064
+ children: [/* @__PURE__ */ jsx("dt", {
9065
+ className: "text-muted-foreground",
9066
+ children: key
9067
+ }), /* @__PURE__ */ jsx("dd", {
9068
+ className: "m-0 break-all text-foreground",
9069
+ children: value
9070
+ })]
9071
+ }, key))
9072
+ })
9073
+ }),
9074
+ /* @__PURE__ */ jsx(CodeBlock, {
9075
+ "data-language": response.json ? "json" : "text",
9076
+ "data-title": labels.apiResponseBody,
9077
+ "data-line-count": String(response.body.split("\n").length),
9078
+ className: "text-xs",
9079
+ children: /* @__PURE__ */ jsx("code", {
9080
+ className: response.json ? "language-json" : "language-text",
9081
+ children: response.body || " "
9082
+ })
9083
+ })
9084
+ ] }) : status !== "error" && /* @__PURE__ */ jsx("p", {
9085
+ className: "m-0 text-muted-foreground",
9086
+ children: labels.apiNoResponse
9087
+ })
9088
+ ]
9089
+ })
9090
+ ]
9091
+ });
9092
+ }
9093
+
8145
9094
  //#endregion
8146
9095
  //#region src/components/OpenApiOperation.tsx
8147
- function FieldChildren({ node }) {
9096
+ function FieldChildren$1({ node }) {
8148
9097
  const labels = useLabels();
8149
9098
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [node.description, node.enum && node.enum.length > 0 && /* @__PURE__ */ jsxs("div", {
8150
9099
  className: "mt-1",
@@ -8159,10 +9108,10 @@ function schemaDetails(node, labels) {
8159
9108
  if (!node.children?.length) return void 0;
8160
9109
  return /* @__PURE__ */ jsx(Expandable, {
8161
9110
  title: node.type === "oneOf" || node.type === "anyOf" ? labels.allowedTypes : labels.properties,
8162
- children: /* @__PURE__ */ jsx(SchemaTable, { nodes: node.children })
9111
+ children: /* @__PURE__ */ jsx(SchemaTable$1, { nodes: node.children })
8163
9112
  });
8164
9113
  }
8165
- function SchemaTable({ nodes }) {
9114
+ function SchemaTable$1({ nodes }) {
8166
9115
  const labels = useLabels();
8167
9116
  return /* @__PURE__ */ jsx(PropertiesTable, { children: nodes.map((node) => /* @__PURE__ */ jsx(PropertiesTable.Row, {
8168
9117
  name: node.name || node.type,
@@ -8171,13 +9120,13 @@ function SchemaTable({ nodes }) {
8171
9120
  deprecated: node.deprecated,
8172
9121
  default: node.default,
8173
9122
  details: schemaDetails(node, labels),
8174
- children: /* @__PURE__ */ jsx(FieldChildren, { node })
9123
+ children: /* @__PURE__ */ jsx(FieldChildren$1, { node })
8175
9124
  }, node.name || node.type)) });
8176
9125
  }
8177
9126
  /** Renders a schema tree: a root object's properties, or the node itself. */
8178
9127
  function SchemaFields({ node }) {
8179
9128
  const isUnion = node.type === "oneOf" || node.type === "anyOf";
8180
- return /* @__PURE__ */ jsx(SchemaTable, { nodes: !node.name && !isUnion && node.children?.length ? node.children : [node] });
9129
+ return /* @__PURE__ */ jsx(SchemaTable$1, { nodes: !node.name && !isUnion && node.children?.length ? node.children : [node] });
8181
9130
  }
8182
9131
  function HighlightedCode({ language, title, html, source, lineCount }) {
8183
9132
  return /* @__PURE__ */ jsx(CodeBlock, {
@@ -8193,40 +9142,57 @@ function HighlightedCode({ language, title, html, source, lineCount }) {
8193
9142
  })
8194
9143
  });
8195
9144
  }
9145
+ /** How a credential is sent, e.g. "Bearer <token>" or "<api-key>". */
9146
+ function credentialExample(scheme) {
9147
+ const placeholder = securityPlaceholder(scheme);
9148
+ if (scheme.type === "apiKey") return placeholder;
9149
+ return `${scheme.type === "http" && scheme.scheme && scheme.scheme !== "bearer" ? scheme.scheme.charAt(0).toUpperCase() + scheme.scheme.slice(1) : "Bearer"} ${placeholder}`;
9150
+ }
8196
9151
  /** The generated reference for one API operation, rendered under the page body. */
8197
- function OpenApiOperation({ operation }) {
9152
+ function OpenApiOperation({ operation, playground: requested = false }) {
8198
9153
  const labels = useLabels();
8199
- const sectionEntries = operationSections(operation);
8200
- const sections = new Map(sectionEntries.map((entry) => [entry.name, entry.id]));
9154
+ const playground = operation.webhook ? false : requested;
9155
+ const options = { playground: !!playground };
9156
+ const sectionEntries = operationSections(operation, labels, options);
9157
+ const sections = new Map(operationSections(operation, void 0, options).map((entry) => [entry.name, entry.id]));
9158
+ const parameterSections = operationParameterSections(operation, labels);
9159
+ const parameterOffset = playground ? 1 : 0;
8201
9160
  return /* @__PURE__ */ jsxs("div", {
8202
9161
  className: "docs-markdown",
8203
9162
  children: [
8204
- operationParameterSections(operation, labels).map(({ location, name }, index) => /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
8205
- id: sectionEntries[index].id,
9163
+ playground && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
9164
+ id: sections.get("Try it"),
9165
+ children: labels.apiPlayground
9166
+ }), /* @__PURE__ */ jsx(ApiPlayground, {
9167
+ operation,
9168
+ proxy: playground.proxy
9169
+ })] }),
9170
+ parameterSections.map(({ location, name }, index) => /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
9171
+ id: sectionEntries[index + parameterOffset].id,
8206
9172
  children: name
8207
- }), /* @__PURE__ */ jsxs(PropertiesTable, { children: [location === "header" && operation.security.length > 0 && /* @__PURE__ */ jsxs(PropertiesTable.Row, {
8208
- name: "Authorization",
9173
+ }), /* @__PURE__ */ jsxs(PropertiesTable, { children: [securityForLocation(operation, location).map((scheme) => /* @__PURE__ */ jsxs(PropertiesTable.Row, {
9174
+ name: securityFieldName(scheme),
8209
9175
  type: "string",
8210
9176
  required: true,
8211
9177
  children: [
8212
- labels.apiCredentials,
9178
+ scheme.description || labels.apiCredentials,
8213
9179
  " ",
8214
- /* @__PURE__ */ jsx("code", { children: "Bearer <token>" }),
9180
+ /* @__PURE__ */ jsx("code", { children: credentialExample(scheme) }),
8215
9181
  "."
8216
9182
  ]
8217
- }), operation.parameters[location].map((parameter) => /* @__PURE__ */ jsx(PropertiesTable.Row, {
9183
+ }, `security:${scheme.name}`)), operation.parameters[location].map((parameter) => /* @__PURE__ */ jsx(PropertiesTable.Row, {
8218
9184
  name: parameter.name || parameter.type,
8219
9185
  type: parameter.type,
8220
9186
  required: parameter.required,
8221
9187
  deprecated: parameter.deprecated,
8222
9188
  default: parameter.default,
8223
9189
  details: schemaDetails(parameter, labels),
8224
- children: /* @__PURE__ */ jsx(FieldChildren, { node: parameter })
9190
+ children: /* @__PURE__ */ jsx(FieldChildren$1, { node: parameter })
8225
9191
  }, parameter.name))] })] }, location)),
8226
9192
  operation.requestBody && /* @__PURE__ */ jsxs("section", { children: [
8227
9193
  /* @__PURE__ */ jsx("h2", {
8228
- id: sections.get("Request body"),
8229
- children: labels.apiRequestBody
9194
+ id: sections.get(operation.webhook ? "Payload" : "Request body"),
9195
+ children: operation.webhook ? labels.apiPayload : labels.apiRequestBody
8230
9196
  }),
8231
9197
  /* @__PURE__ */ jsx(SchemaFields, { node: operation.requestBody.schema }),
8232
9198
  operation.requestBody.example && /* @__PURE__ */ jsx(HighlightedCode, {
@@ -8276,6 +9242,67 @@ function OpenApiOperation({ operation }) {
8276
9242
  });
8277
9243
  }
8278
9244
 
9245
+ //#endregion
9246
+ //#region src/components/OpenApiSchema.tsx
9247
+ function FieldChildren({ node }) {
9248
+ const labels = useLabels();
9249
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [node.description, node.enum && node.enum.length > 0 && /* @__PURE__ */ jsxs("div", {
9250
+ className: "mt-1",
9251
+ children: [
9252
+ labels.fieldOptions,
9253
+ " ",
9254
+ node.enum.map((value, index) => /* @__PURE__ */ jsxs("span", { children: [index > 0 && ", ", /* @__PURE__ */ jsx("code", { children: value })] }, value))
9255
+ ]
9256
+ })] });
9257
+ }
9258
+ function SchemaTable({ nodes, labels }) {
9259
+ return /* @__PURE__ */ jsx(PropertiesTable, { children: nodes.map((node) => /* @__PURE__ */ jsx(PropertiesTable.Row, {
9260
+ name: node.name || node.type,
9261
+ type: node.type,
9262
+ required: node.required,
9263
+ deprecated: node.deprecated,
9264
+ default: node.default,
9265
+ details: node.children?.length ? /* @__PURE__ */ jsx(Expandable, {
9266
+ title: node.type === "oneOf" || node.type === "anyOf" ? labels.allowedTypes : labels.properties,
9267
+ children: /* @__PURE__ */ jsx(SchemaTable, {
9268
+ nodes: node.children,
9269
+ labels
9270
+ })
9271
+ }) : void 0,
9272
+ children: /* @__PURE__ */ jsx(FieldChildren, { node })
9273
+ }, node.name || node.type)) });
9274
+ }
9275
+ /** The generated reference for one named component schema. */
9276
+ function OpenApiSchema({ page }) {
9277
+ const labels = useLabels();
9278
+ const sections = new Map(schemaSections(page, labels).map((entry) => [entry.id, entry.name]));
9279
+ const properties = sections.get("properties");
9280
+ const example = sections.get("example");
9281
+ return /* @__PURE__ */ jsxs("div", {
9282
+ className: "docs-markdown",
9283
+ children: [properties && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
9284
+ id: "properties",
9285
+ children: properties
9286
+ }), /* @__PURE__ */ jsx(SchemaTable, {
9287
+ nodes: page.schema.children ?? [],
9288
+ labels
9289
+ })] }), example && page.example && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
9290
+ id: "example",
9291
+ children: example
9292
+ }), /* @__PURE__ */ jsx(CodeBlock, {
9293
+ "data-language": "json",
9294
+ "data-title": page.name,
9295
+ children: page.exampleHtml ? /* @__PURE__ */ jsx("code", {
9296
+ className: "language-json",
9297
+ dangerouslySetInnerHTML: { __html: page.exampleHtml }
9298
+ }) : /* @__PURE__ */ jsx("code", {
9299
+ className: "language-json",
9300
+ children: page.example
9301
+ })
9302
+ })] })]
9303
+ });
9304
+ }
9305
+
8279
9306
  //#endregion
8280
9307
  //#region src/components/PageActions.tsx
8281
9308
  function editHref(template, filePath) {
@@ -8409,6 +9436,8 @@ function DocContent({ page, doc, site }) {
8409
9436
  const contextualOptions = resolveContextualOptions(site.contextualOptions, page, site.labels);
8410
9437
  const related = resolveRelated(doc.frontmatter?.related);
8411
9438
  const operation = getOperation(doc.frontmatter?.openapi);
9439
+ const schema = operation ? void 0 : getSchema(doc.frontmatter?.["openapi-schema"]);
9440
+ const playground = operation && resolvePlaygroundDisplay(site.api.playground, doc.frontmatter) === "interactive" ? { proxy: site.api.playground.proxy } : false;
8412
9441
  const dateFormat = new Intl.DateTimeFormat(resolveLocale(page.language, site.locale), {
8413
9442
  dateStyle: "medium",
8414
9443
  timeZone: "UTC"
@@ -8442,10 +9471,10 @@ function DocContent({ page, doc, site }) {
8442
9471
  className: "mt-3 flex flex-wrap items-center gap-2",
8443
9472
  children: [
8444
9473
  /* @__PURE__ */ jsx(Badge, {
8445
- color: methodColor(operation.method),
9474
+ color: methodColor(operation.webhook ? "WEBHOOK" : operation.method),
8446
9475
  size: "sm",
8447
9476
  className: "font-mono",
8448
- children: operation.method
9477
+ children: operation.webhook ? "WEBHOOK" : operation.method
8449
9478
  }),
8450
9479
  /* @__PURE__ */ jsx("code", {
8451
9480
  className: "font-mono text-muted-foreground text-sm",
@@ -8467,7 +9496,11 @@ function DocContent({ page, doc, site }) {
8467
9496
  className: "docs-markdown",
8468
9497
  children: /* @__PURE__ */ jsx(Content, {})
8469
9498
  }),
8470
- operation && /* @__PURE__ */ jsx(OpenApiOperation, { operation }),
9499
+ operation && /* @__PURE__ */ jsx(OpenApiOperation, {
9500
+ operation,
9501
+ playground
9502
+ }),
9503
+ schema && /* @__PURE__ */ jsx(OpenApiSchema, { page: schema }),
8471
9504
  /* @__PURE__ */ jsx(PageActions, {
8472
9505
  page,
8473
9506
  frontmatter: doc.frontmatter,
@@ -8814,7 +9847,7 @@ function NavNodes({ nodes, pathname, depth, drilldown, expandLabel, collapseLabe
8814
9847
  color: methodColor(method),
8815
9848
  size: "xs",
8816
9849
  className: "w-10 shrink-0 justify-center whitespace-nowrap font-mono",
8817
- children: method === "DELETE" ? "DEL" : method
9850
+ children: methodBadgeText(method)
8818
9851
  }) : null,
8819
9852
  label,
8820
9853
  !method && tag ? /* @__PURE__ */ jsx(Badge$1, {
@@ -8948,7 +9981,8 @@ function Docs({ page, doc, site }) {
8948
9981
  }), /* @__PURE__ */ jsx(Footer, { footer: site.footer })]
8949
9982
  });
8950
9983
  const operation = getOperation(doc.frontmatter?.openapi);
8951
- const toc = operation ? [...doc.toc || [], ...operationSections(operation, site.labels)] : doc.toc;
9984
+ const schema = operation ? void 0 : getSchema(doc.frontmatter?.["openapi-schema"]);
9985
+ const toc = operation ? [...doc.toc || [], ...operationSections(operation, site.labels, { playground: resolvePlaygroundDisplay(site.api.playground, doc.frontmatter) === "interactive" })] : schema ? [...doc.toc || [], ...schemaSections(schema, site.labels)] : doc.toc;
8952
9986
  return /* @__PURE__ */ jsx(PanelProvider, { children: /* @__PURE__ */ jsx(DocsBody, {
8953
9987
  page,
8954
9988
  doc,