radiant-docs 0.1.60 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "radiant-docs",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "description": "CLI tool for previewing Radiant documentation locally",
5
5
  "type": "module",
6
6
  "bin": {
@@ -111,13 +111,29 @@ const formattedBodyDescription = bodyDescription
111
111
  />
112
112
  )
113
113
  }
114
- <div class="xl:hidden space-y-6 mt-6">
115
- <RequestSnippets
116
- api={api}
117
- method={route.openApiMethod}
118
- path={route.openApiPath}
119
- />
120
- {responses && <ResponseSnippets responses={responses} />}
114
+ <div data-inline-snippet-stack class="xl:hidden space-y-6 mt-6">
115
+ <div
116
+ data-snippet-slot
117
+ data-inline-snippet-slot
118
+ class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
119
+ >
120
+ <RequestSnippets
121
+ api={api}
122
+ method={route.openApiMethod}
123
+ path={route.openApiPath}
124
+ />
125
+ </div>
126
+ {
127
+ responses && (
128
+ <div
129
+ data-snippet-slot
130
+ data-inline-snippet-slot
131
+ class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
132
+ >
133
+ <ResponseSnippets responses={responses} />
134
+ </div>
135
+ )
136
+ }
121
137
  </div>
122
138
  <div class="mt-10">
123
139
  <!-- Request -->
@@ -157,10 +173,9 @@ const formattedBodyDescription = bodyDescription
157
173
  />
158
174
  )}
159
175
  {hasCommonFields && (
160
- <div x-data="{ expanded: false }" class="mt-4">
176
+ <div x-data="{ expanded: true }" class="mt-4">
161
177
  <div
162
- class="w-full overflow-hidden rounded-xl border border-neutral-200 bg-white transition-colors duration-200 dark:border-neutral-800 dark:bg-(--rd-code-surface)"
163
- x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
178
+ class="w-full overflow-hidden rounded-xl border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
164
179
  >
165
180
  <button
166
181
  type="button"
@@ -201,10 +216,9 @@ const formattedBodyDescription = bodyDescription
201
216
  <div class="mb-2 text-xs font-medium text-neutral-600 dark:text-neutral-400">
202
217
  {variant.label}
203
218
  </div>
204
- <div x-data="{ expanded: false }">
219
+ <div x-data="{ expanded: true }">
205
220
  <div
206
- class="w-full overflow-hidden rounded-lg border border-neutral-200 bg-white transition-colors duration-200 dark:border-neutral-800 dark:bg-(--rd-code-surface)"
207
- x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
221
+ class="w-full overflow-hidden rounded-lg border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
208
222
  >
209
223
  <button
210
224
  type="button"
@@ -485,4 +499,138 @@ const formattedBodyDescription = bodyDescription
485
499
  );
486
500
  applySlotHeights();
487
501
  }
502
+
503
+ const inlineStack = document.querySelector("[data-inline-snippet-stack]");
504
+
505
+ if (inlineStack instanceof HTMLElement) {
506
+ const inlineSlots = Array.from(
507
+ inlineStack.querySelectorAll("[data-inline-snippet-slot]"),
508
+ ).filter((slot) => slot instanceof HTMLElement);
509
+
510
+ const getInlineViewportHeight = () => {
511
+ if (window.visualViewport?.height) {
512
+ return Math.floor(window.visualViewport.height);
513
+ }
514
+ return Math.floor(window.innerHeight);
515
+ };
516
+
517
+ const getInlineSlotHeightCap = () => {
518
+ const viewportCap = Math.floor(getInlineViewportHeight() * 0.7);
519
+ return Math.max(0, Math.min(448, viewportCap));
520
+ };
521
+
522
+ const inlinePrefersReducedMotion =
523
+ typeof window.matchMedia === "function" &&
524
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
525
+ const inlineSlotHeightTransitionMs = 360;
526
+ let isAnimatingInlineSlotHeights = false;
527
+ let inlineSlotHeightAnimationTimeoutId = null;
528
+
529
+ const resetInlineSlotHeights = () => {
530
+ for (const slot of inlineSlots) {
531
+ slot.style.height = "";
532
+ slot.style.maxHeight = "";
533
+ }
534
+ };
535
+
536
+ const applyInlineSlotHeights = ({ animate = false } = {}) => {
537
+ if (!inlineSlots.length) return;
538
+ if (isAnimatingInlineSlotHeights && !animate) return;
539
+
540
+ if (window.getComputedStyle(inlineStack).display === "none") {
541
+ resetInlineSlotHeights();
542
+ return;
543
+ }
544
+
545
+ const shouldAnimate = animate && !inlinePrefersReducedMotion;
546
+ const previousHeights = shouldAnimate
547
+ ? inlineSlots.map((slot) =>
548
+ Math.ceil(slot.getBoundingClientRect().height),
549
+ )
550
+ : [];
551
+ const previousTransitions = shouldAnimate
552
+ ? inlineSlots.map((slot) => slot.style.transition)
553
+ : [];
554
+ const heightCap = getInlineSlotHeightCap();
555
+
556
+ if (shouldAnimate) {
557
+ for (const slot of inlineSlots) {
558
+ slot.style.transition = "none";
559
+ }
560
+ }
561
+
562
+ for (const slot of inlineSlots) {
563
+ slot.style.height = "auto";
564
+ slot.style.maxHeight = "none";
565
+ }
566
+
567
+ const targetHeights = inlineSlots.map((slot) => {
568
+ const naturalHeight = Math.ceil(slot.scrollHeight);
569
+ return Math.min(naturalHeight, heightCap);
570
+ });
571
+
572
+ const setInlineSlotHeights = () => {
573
+ for (let i = 0; i < inlineSlots.length; i += 1) {
574
+ const height = targetHeights[i];
575
+ inlineSlots[i].style.height = `${height}px`;
576
+ inlineSlots[i].style.maxHeight = `${height}px`;
577
+ }
578
+ };
579
+
580
+ if (!shouldAnimate) {
581
+ setInlineSlotHeights();
582
+ return;
583
+ }
584
+
585
+ isAnimatingInlineSlotHeights = true;
586
+ if (inlineSlotHeightAnimationTimeoutId) {
587
+ window.clearTimeout(inlineSlotHeightAnimationTimeoutId);
588
+ }
589
+
590
+ for (let i = 0; i < inlineSlots.length; i += 1) {
591
+ const height = previousHeights[i];
592
+ inlineSlots[i].style.height = `${height}px`;
593
+ inlineSlots[i].style.maxHeight = `${height}px`;
594
+ }
595
+
596
+ void inlineStack.offsetHeight;
597
+
598
+ for (let i = 0; i < inlineSlots.length; i += 1) {
599
+ inlineSlots[i].style.transition = previousTransitions[i];
600
+ }
601
+
602
+ window.requestAnimationFrame(setInlineSlotHeights);
603
+
604
+ inlineSlotHeightAnimationTimeoutId = window.setTimeout(() => {
605
+ isAnimatingInlineSlotHeights = false;
606
+ inlineSlotHeightAnimationTimeoutId = null;
607
+ applyInlineSlotHeights();
608
+ }, inlineSlotHeightTransitionMs + 80);
609
+ };
610
+
611
+ let inlineSlotAnimationFrameId = null;
612
+ const scheduleInlineSlotHeights = (options = {}) => {
613
+ if (inlineSlotAnimationFrameId) {
614
+ window.cancelAnimationFrame(inlineSlotAnimationFrameId);
615
+ }
616
+
617
+ inlineSlotAnimationFrameId = window.requestAnimationFrame(() => {
618
+ inlineSlotAnimationFrameId = window.requestAnimationFrame(() => {
619
+ inlineSlotAnimationFrameId = null;
620
+ applyInlineSlotHeights(options);
621
+ });
622
+ });
623
+ };
624
+
625
+ const inlineResizeObserver = new ResizeObserver(scheduleInlineSlotHeights);
626
+ inlineResizeObserver.observe(inlineStack);
627
+
628
+ window.addEventListener("resize", scheduleInlineSlotHeights);
629
+ window.visualViewport?.addEventListener("resize", scheduleInlineSlotHeights);
630
+ window.addEventListener(
631
+ "rd:snippet-content-change",
632
+ () => scheduleInlineSlotHeights({ animate: true }),
633
+ );
634
+ scheduleInlineSlotHeights();
635
+ }
488
636
  </script>
@@ -1963,7 +1963,7 @@ export default function AssistantEmbedPanel({
1963
1963
  <AssistantPanelIcon
1964
1964
  color={launcherIconColor}
1965
1965
  imageSrc={launcherIconImageSrc}
1966
- className="assistant-embed-header-icon inline-flex size-9 shrink-0 items-center justify-center rounded-md dark:border-[0.5px] border-neutral-900/7 bg-radial from-neutral-700/5 dark:from-white/7 to-neutral-900/7 dark:to-white/5 to-60% text-neutral-900 dark:border-white/7 dark:text-neutral-50 [&_img]:block [&_img]:size-5.5 [&_img]:object-contain [&_svg]:static [&_svg]:block [&_svg]:size-5 [&_svg]:transform-none [&_svg]:opacity-100"
1966
+ className="assistant-embed-header-icon inline-flex size-9 shrink-0 items-center justify-center rounded-md dark:border-[0.5px] border-neutral-900/7 bg-neutral-700/7 dark:bg-white/6 text-neutral-900 dark:border-white/7 dark:text-neutral-50 [&_img]:block [&_img]:size-5.5 [&_img]:object-contain [&_svg]:static [&_svg]:block [&_svg]:size-5 [&_svg]:transform-none [&_svg]:opacity-100"
1967
1967
  />
1968
1968
  <div className="min-w-0 space-y-px">
1969
1969
  <p className="truncate text-sm font-medium leading-3.5 text-neutral-900 dark:text-neutral-50">
@@ -2198,14 +2198,25 @@ export default function AssistantEmbedPanel({
2198
2198
  </p>
2199
2199
  <button
2200
2200
  type="button"
2201
- className="mt-1 inline-flex items-center gap-1.5 rounded-md border border-neutral-900/8 bg-white px-2.5 py-1.5 text-[13px] font-medium text-neutral-700 shadow-xs transition hover:bg-neutral-50 dark:border-white/10 dark:bg-white/5 dark:text-neutral-200 dark:hover:bg-white/10 cursor-pointer"
2201
+ className="mt-1 inline-flex items-center gap-1.5 rounded-md border border-neutral-900/8 bg-white px-2.5 py-1.5 text-[13px] font-medium text-neutral-700 shadow-xs transition hover:bg-neutral-50/80 dark:border-white/10 dark:bg-white/5 dark:text-neutral-200 dark:hover:bg-white/10 cursor-pointer"
2202
2202
  onClick={handleUnavailableBack}
2203
2203
  >
2204
- <Icon
2205
- icon="lucide:arrow-left"
2206
- className="size-3.5"
2207
- aria-hidden="true"
2208
- />
2204
+ <svg
2205
+ xmlns="http://www.w3.org/2000/svg"
2206
+ width="14px"
2207
+ height="14px"
2208
+ viewBox="0 0 24 24"
2209
+ >
2210
+ <path d="M0 0h24v24H0z" fill="none" />
2211
+ <path
2212
+ fill="none"
2213
+ stroke="currentColor"
2214
+ stroke-linecap="round"
2215
+ stroke-linejoin="round"
2216
+ stroke-width="2"
2217
+ d="m12 19l-7-7l7-7m7 7H5"
2218
+ />
2219
+ </svg>
2209
2220
  Back
2210
2221
  </button>
2211
2222
  </div>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { Icon } from "astro-icon/components";
3
- import { methodColors } from "../../lib/utils";
3
+ import { methodTagColors } from "../../lib/utils";
4
4
  import type { OpenApiRoute } from "../../lib/routes";
5
5
 
6
6
  interface Props {
@@ -9,18 +9,26 @@ interface Props {
9
9
  }
10
10
 
11
11
  const { route, serverUrl } = Astro.props;
12
+ const methodColor =
13
+ methodTagColors[route.openApiMethod.toLowerCase()] ?? methodTagColors.get;
14
+
15
+ const pathParts = route.openApiPath
16
+ .split(/(\{[^}]+\})/g)
17
+ .filter(Boolean)
18
+ .map((text) => ({
19
+ text,
20
+ isParam: text.startsWith("{") && text.endsWith("}"),
21
+ }));
12
22
  ---
13
23
 
14
24
  <div
15
- class="min-w-0 flex-1 flex items-center p-1 border border-neutral-200 bg-white rounded-xl shadow-xs overflow-hidden dark:border-neutral-800 dark:bg-(--rd-code-surface)"
25
+ class="min-w-0 flex-1 flex items-center p-1 border-[0.5px] border-neutral-900/8 bg-white rounded-xl shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] overflow-hidden dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
16
26
  >
17
27
  <span
18
- class:list={[
19
- "shrink-0 inline-block px-1.5 ml-1 text-sm font-semibold rounded-md uppercase border",
20
- methodColors[route.openApiMethod.toLowerCase()] || methodColors.get,
21
- ]}
28
+ class="rd-endpoint-method-tag shrink-0 inline-block px-1.5 ml-1 text-sm font-semibold rounded-md uppercase border"
29
+ style={`--rd-tag-color-light:${methodColor};--rd-tag-color-dark:${methodColor};`}
22
30
  >
23
- {route.openApiMethod}
31
+ {route.openApiMethod.toUpperCase()}
24
32
  </span>
25
33
  <code
26
34
  class="group flex-1 mx-2 h-[30px] flex items-center text-[13px] text-neutral-600 dark:text-neutral-300 min-w-0 relative cursor-pointer"
@@ -41,8 +49,23 @@ const { route, serverUrl } = Astro.props;
41
49
  }
42
50
  }`
43
51
  >
44
- <span class="truncate min-w-0 flex-1">
45
- {route.openApiPath}
52
+ <span
53
+ class="flex min-w-0 flex-1 items-center overflow-hidden whitespace-nowrap"
54
+ >
55
+ {
56
+ pathParts.map((part) =>
57
+ part.isParam ? (
58
+ <span
59
+ class="rd-endpoint-path-param inline-block rounded-sm border p-px leading-none"
60
+ style={`--rd-tag-color-light:${methodColor};--rd-tag-color-dark:${methodColor};`}
61
+ >
62
+ {part.text}
63
+ </span>
64
+ ) : (
65
+ <span>{part.text}</span>
66
+ ),
67
+ )
68
+ }
46
69
  </span>
47
70
  <div
48
71
  class="absolute right-0 top-1/2 -translate-y-1/2 flex items-center gap-1 text-[12px] px-1.5 py-px bg-white border border-neutral-200 rounded-md duration-200 opacity-0 scale-75 group-hover:scale-100 group-hover:opacity-100 group-hover:duration-200 group-hover:ease-out group-hover:delay-75 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300"
@@ -62,3 +85,25 @@ const { route, serverUrl } = Astro.props;
62
85
  </code>
63
86
  <slot />
64
87
  </div>
88
+
89
+ <style>
90
+ .rd-endpoint-method-tag,
91
+ .rd-endpoint-path-param {
92
+ --rd-tag-color: var(--rd-tag-color-light, var(--color-theme));
93
+ background-color: color-mix(in oklab, var(--rd-tag-color) 8%, transparent);
94
+ border-color: color-mix(in oklab, var(--rd-tag-color) 0%, transparent);
95
+ color: color-mix(in oklab, var(--rd-tag-color) 95%, transparent);
96
+ }
97
+
98
+ :global(.dark) .rd-endpoint-method-tag,
99
+ :global(.dark) .rd-endpoint-path-param,
100
+ :global([data-theme="dark"]) .rd-endpoint-method-tag,
101
+ :global([data-theme="dark"]) .rd-endpoint-path-param {
102
+ --rd-tag-color: var(
103
+ --rd-tag-color-dark,
104
+ var(--rd-tag-color-light, var(--color-theme))
105
+ );
106
+ background-color: color-mix(in oklab, var(--rd-tag-color) 12%, transparent);
107
+ color: color-mix(in oklab, var(--rd-tag-color) 85%, transparent);
108
+ }
109
+ </style>
@@ -587,7 +587,7 @@ const sectionVariantFieldNames = Object.fromEntries(
587
587
  }
588
588
 
589
589
  return (
590
- <div class="border border-neutral-200 bg-white shadow-xs rounded-xl p-4 pb-0 dark:border-neutral-800 dark:bg-(--rd-code-surface) [&_[role='region']]:border-b-0">
590
+ <div class="border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] rounded-xl p-4 pb-0 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)] [&_[role='region']]:border-b-0">
591
591
  <Accordion title={headers[key]} defaultOpen titleSize="xl">
592
592
  {key === "body" && formattedBodyDescription && (
593
593
  <div
@@ -593,7 +593,12 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
593
593
  </div>
594
594
  </div>
595
595
 
596
- <div class="relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl rounded-tl-xl border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface)">
596
+ <div
597
+ class:list={[
598
+ "relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface)",
599
+ hasMultipleRequests && "rounded-tl-xl",
600
+ ]}
601
+ >
597
602
  <div
598
603
  x-ref="snippetPanels"
599
604
  class="relative h-full overflow-auto transition-[height] motion-reduce:transition-none [scrollbar-width:thin] [scrollbar-color:var(--color-neutral-300)_transparent] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-neutral-300/70 hover:[&::-webkit-scrollbar-thumb]:bg-neutral-300/90 dark:[scrollbar-color:var(--color-neutral-700)_transparent] dark:[&::-webkit-scrollbar-thumb]:bg-neutral-700/70 dark:hover:[&::-webkit-scrollbar-thumb]:bg-neutral-700/90"
@@ -46,7 +46,9 @@ function countImmediateChildren(field: ResponseField): number {
46
46
  }
47
47
  ---
48
48
 
49
- <div class="flex flex-col gap-4 divide-y divide-neutral-100 dark:divide-neutral-800 *:pb-3 *:last:pb-0">
49
+ <div
50
+ class="flex flex-col gap-4 divide-y divide-neutral-100 dark:divide-neutral-800 *:pb-3 *:last:pb-0"
51
+ >
50
52
  {
51
53
  fields.map((field) => {
52
54
  const revealedFieldCount = countImmediateChildren(field);
@@ -69,10 +71,7 @@ function countImmediateChildren(field: ResponseField): number {
69
71
  exclusiveMaximum={field.exclusiveMaximum}
70
72
  />
71
73
  {hasExpandableContent && (
72
- <div
73
- class="mt-2 w-full overflow-hidden rounded-lg border border-neutral-200 bg-white transition-colors duration-200 dark:border-neutral-800 dark:bg-(--rd-code-surface)"
74
- x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
75
- >
74
+ <div class="mt-2.5 w-full overflow-hidden rounded-lg border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]">
76
75
  <button
77
76
  type="button"
78
77
  x-on:click="expanded = !expanded"
@@ -83,19 +82,21 @@ function countImmediateChildren(field: ResponseField): number {
83
82
  <ListChevronsToggle class="size-4 shrink-0 text-neutral-400 group-hover:text-neutral-600 transition duration-200 dark:text-neutral-500 dark:group-hover:text-neutral-300" />
84
83
  <span>
85
84
  {revealedFieldCount}{" "}
86
- {revealedFieldCount === 1
87
- ? "field"
88
- : "fields"}
85
+ {revealedFieldCount === 1 ? "field" : "fields"}
89
86
  </span>
90
87
  </span>
91
88
  </button>
92
89
  <div x-show="expanded" x-collapse x-cloak>
93
90
  <div class="border-t border-neutral-100 px-3 py-3 dark:border-neutral-800">
94
- {field.nested && field.nested.length > 0 && nestedFieldCount > 0 && (
95
- <Astro.self fields={field.nested} depth={depth + 1} />
96
- )}
91
+ {field.nested &&
92
+ field.nested.length > 0 &&
93
+ nestedFieldCount > 0 && (
94
+ <Astro.self fields={field.nested} depth={depth + 1} />
95
+ )}
97
96
  {field.variants && field.variants.length > 0 && (
98
- <div class:list={["space-y-2", nestedFieldCount > 0 && "mt-3"]}>
97
+ <div
98
+ class:list={["space-y-2", nestedFieldCount > 0 && "mt-3"]}
99
+ >
99
100
  <p class="text-xs text-neutral-500 dark:text-neutral-400">
100
101
  {field.variantType === "anyOf"
101
102
  ? "One or more variants may apply."
@@ -107,7 +108,10 @@ function countImmediateChildren(field: ResponseField): number {
107
108
  <div class="mb-2 text-xs font-medium text-neutral-600 dark:text-neutral-400">
108
109
  {variant.label}
109
110
  </div>
110
- <Astro.self fields={variant.fields} depth={depth + 1} />
111
+ <Astro.self
112
+ fields={variant.fields}
113
+ depth={depth + 1}
114
+ />
111
115
  </div>
112
116
  {field.variantType === "oneOf" &&
113
117
  index < field.variants.length - 1 && (
@@ -771,10 +771,9 @@ Object.entries(responses)
771
771
  {response.variants && response.variants.length > 0 && (
772
772
  <p class="mb-2 text-xs text-neutral-500 dark:text-neutral-400">Common fields</p>
773
773
  )}
774
- <div x-data="{ expanded: false }">
774
+ <div x-data="{ expanded: true }">
775
775
  <div
776
- class="w-full overflow-hidden rounded-xl border border-neutral-200 bg-white transition-colors duration-200 dark:border-neutral-800 dark:bg-(--rd-code-surface)"
777
- x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
776
+ class="w-full overflow-hidden rounded-xl border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
778
777
  >
779
778
  <button
780
779
  type="button"
@@ -814,10 +813,9 @@ Object.entries(responses)
814
813
  <div class="mb-2 text-xs font-medium text-neutral-600 dark:text-neutral-400">
815
814
  {variant.label}
816
815
  </div>
817
- <div x-data="{ expanded: false }">
816
+ <div x-data="{ expanded: true }">
818
817
  <div
819
- class="w-full overflow-hidden rounded-lg border border-neutral-200 bg-white transition-colors duration-200 dark:border-neutral-800 dark:bg-(--rd-code-surface)"
820
- x-bind:class="expanded ? 'border-neutral-300 dark:border-neutral-700' : 'border-neutral-200 dark:border-neutral-800'"
818
+ class="w-full overflow-hidden rounded-lg border-[0.5px] border-neutral-900/8 bg-white shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] transition-colors duration-200 dark:border-white/6 dark:bg-(--rd-code-surface) dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]"
821
819
  >
822
820
  <button
823
821
  type="button"
@@ -513,7 +513,12 @@ const hasMultipleResponses = responseSnippetItems.length > 1;
513
513
  </div>
514
514
  </div>
515
515
 
516
- <div class="relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl rounded-tl-xl border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface)">
516
+ <div
517
+ class:list={[
518
+ "relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface)",
519
+ hasMultipleResponses && "rounded-tl-xl",
520
+ ]}
521
+ >
517
522
  <div
518
523
  x-ref="snippetPanels"
519
524
  class="relative h-full overflow-auto transition-[height] motion-reduce:transition-none [scrollbar-width:thin] [scrollbar-color:var(--color-neutral-300)_transparent] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-neutral-300/70 hover:[&::-webkit-scrollbar-thumb]:bg-neutral-300/90 dark:[scrollbar-color:var(--color-neutral-700)_transparent] dark:[&::-webkit-scrollbar-thumb]:bg-neutral-700/70 dark:hover:[&::-webkit-scrollbar-thumb]:bg-neutral-700/90"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import Tag from "../ui/Tag.astro";
3
- import { methodColors } from "../../lib/utils";
3
+ import { methodTagColors } from "../../lib/utils";
4
4
  import { getOpenApiEndpointRouteHref } from "../../lib/routes";
5
5
  import type { NavTag } from "../../lib/validation";
6
6
 
@@ -34,6 +34,10 @@ const href = await getOpenApiEndpointRouteHref({
34
34
 
35
35
  // Use summary for title, fallback to method + path
36
36
  const text = title || summary || `${normalizedMethod.toUpperCase()} ${pathStr}`;
37
+ const methodLabel = (
38
+ normalizedMethod !== "delete" ? normalizedMethod : "del"
39
+ ).toUpperCase();
40
+ const methodColor = methodTagColors[normalizedMethod] ?? methodTagColors.get;
37
41
 
38
42
  // Normalize paths for comparison (remove trailing slashes)
39
43
  const currentPath = Astro.url.pathname.replace(/\/$/, "");
@@ -44,22 +48,15 @@ const isActive = currentPath === targetPath;
44
48
  <a
45
49
  href={href}
46
50
  class:list={[
47
- "flex items-center px-2 py-[7px] text-sm relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:duration-150",
51
+ "flex items-center gap-2 px-2 py-[7px] text-sm relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:duration-150",
48
52
  isActive
49
53
  ? "before:bg-neutral-200/50 dark:before:bg-neutral-800 text-neutral-900 dark:text-neutral-200"
50
54
  : "text-neutral-600 dark:text-neutral-400 hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50 hover:text-neutral-900 dark:hover:text-neutral-300",
51
55
  ]}
52
56
  >
53
- <span
54
- class:list={[
55
- "px-1 py-px mr-1.5 border rounded-md text-[10px] font-semibold uppercase",
56
- methodColors[normalizedMethod] ?? methodColors.get,
57
- ]}
58
- >
59
- {normalizedMethod !== "delete" ? normalizedMethod : "del"}
60
- </span>
61
- <div class="flex items-center gap-2 min-w-0">
62
- <span>{text}</span>
57
+ <div class="flex min-w-0 flex-1 items-center gap-2">
58
+ <span class="min-w-0 truncate">{text}</span>
63
59
  {tag && <Tag tag={tag} />}
64
60
  </div>
61
+ <Tag color={methodColor}>{methodLabel}</Tag>
65
62
  </a>
@@ -31,7 +31,7 @@ const openApiDoc = await loadOpenApiSpec(openApiPath);
31
31
 
32
32
  // Helper function to parse endpoint string
33
33
  function parseEndpointString(
34
- endpointStr: string
34
+ endpointStr: string,
35
35
  ): { method: string; path: string } | null {
36
36
  const trimmed = endpointStr.trim();
37
37
  const parts = trimmed.split(/\s+/);
@@ -57,7 +57,7 @@ function shouldIncludeEndpoint(
57
57
  method: string,
58
58
  pathStr: string,
59
59
  include?: string[],
60
- exclude?: string[]
60
+ exclude?: string[],
61
61
  ): boolean {
62
62
  const normalizedMethod = method.toUpperCase();
63
63
  const normalizedPath = pathStr.toLowerCase();
@@ -168,13 +168,7 @@ const sortedTagGroups: TagGroup[] = Array.from(tagGroups.entries())
168
168
  // If there are tagged groups, render them (including "Other" if it exists)
169
169
  hasTaggedGroups
170
170
  ? sortedTagGroups.map((group, index) => (
171
- <li
172
- class={
173
- index > 0
174
- ? "mt-5 pt-[25px] relative before:absolute before:top-0 before:inset-x-0 before:h-px before:bg-linear-[90deg,transparent,var(--color-neutral-200)_20%,var(--color-neutral-200)_80%,transparent] dark:before:bg-linear-[90deg,transparent,var(--color-neutral-700)_20%,var(--color-neutral-700)_80%,transparent]"
175
- : ""
176
- }
177
- >
171
+ <li class="my-8 first:mt-0 last:mb-0">
178
172
  <div class="text-sm font-semibold mb-2 flex items-center gap-2 px-2">
179
173
  {group.tag}
180
174
  </div>
@@ -1,5 +1,6 @@
1
1
  ---
2
- import { renderMarkdown } from "../../lib/utils";
2
+ import { methodTagColors, renderMarkdown } from "../../lib/utils";
3
+ import Tag from "./Tag.astro";
3
4
 
4
5
  interface Props {
5
6
  name: string;
@@ -73,6 +74,9 @@ if (hasMinLength && hasMaxLength) {
73
74
  } else if (hasMaxLength) {
74
75
  stringLengthLabel = `length <= ${maxLength}`;
75
76
  }
77
+
78
+ const codeChipClass =
79
+ "mx-px px-[5px] pr-1 bg-neutral-100/80 text-neutral-700/90 rounded-sm leading-none font-mono font-normal border border-neutral-900/4 after:hidden before:hidden dark:bg-neutral-800/90 dark:text-neutral-200/90 dark:border-white/3";
76
80
  ---
77
81
 
78
82
  <div class="space-y-3">
@@ -80,15 +84,14 @@ if (hasMinLength && hasMaxLength) {
80
84
  <span class="font-medium leading-4">
81
85
  {name}
82
86
  </span>
83
- <code
84
- class="text-[10px] font-medium text-neutral-500 border border-neutral-200/80 bg-neutral-50 px-1 rounded-sm dark:text-neutral-300 dark:border-neutral-700/70 dark:bg-neutral-900/70"
85
- >{type}</code
86
- >
87
+ <code class:list={["text-[10px] py-[1.5px]", codeChipClass]}>
88
+ {type}
89
+ </code>
87
90
  {
88
91
  required && (
89
- <div class="text-red-700/70 bg-red-50 border border-red-700/10 rounded-full px-1.5 text-[10px] font-mono font-medium leading-none py-0.5 pb-0.5 h-fit dark:text-red-300 dark:bg-red-950/40 dark:border-red-900/40">
92
+ <Tag color={methodTagColors.delete} class="text-[10px]!">
90
93
  required
91
- </div>
94
+ </Tag>
92
95
  )
93
96
  }
94
97
  {
@@ -114,14 +117,14 @@ if (hasMinLength && hasMaxLength) {
114
117
  <span class="font-medium text-xs">Options:</span>
115
118
  {enumValues.map((v, i, a) => {
116
119
  return (
117
- <div class="flex">
118
- <span class="text-[11px] font-medium text-neutral-600 border border-neutral-200 bg-neutral-50 px-1 py-px rounded-md dark:text-neutral-300 dark:border-neutral-700 dark:bg-neutral-900/70">
119
- <code>{v}</code>
120
- </span>
120
+ <div class="inline-flex items-center leading-none">
121
+ <code class:list={["text-[11px] py-[2px]", codeChipClass]}>
122
+ {v}
123
+ </code>
121
124
  {a.length - 2 === i ? (
122
- <span class="ml-1"> or </span>
125
+ <span class="ml-1 leading-none"> or </span>
123
126
  ) : (
124
- a.length - 1 !== i && <span>, </span>
127
+ a.length - 1 !== i && <span class="leading-none">, </span>
125
128
  )}
126
129
  </div>
127
130
  );
@@ -133,7 +136,7 @@ if (hasMinLength && hasMaxLength) {
133
136
  numericRangeLabel && (
134
137
  <div class="text-sm">
135
138
  <span class="font-medium text-xs">Range:</span>
136
- <code class="text-[11px] font-medium text-neutral-600 border border-neutral-200 bg-neutral-50 px-1 py-px rounded-md dark:text-neutral-300 dark:border-neutral-700 dark:bg-neutral-900/70">
139
+ <code class:list={["text-[11px]", codeChipClass]}>
137
140
  {numericRangeLabel}
138
141
  </code>
139
142
  </div>
@@ -143,7 +146,7 @@ if (hasMinLength && hasMaxLength) {
143
146
  stringLengthLabel && (
144
147
  <div class="text-sm">
145
148
  <span class="font-medium text-xs">Length:</span>
146
- <code class="text-[11px] font-medium text-neutral-600 border border-neutral-200 bg-neutral-50 px-1 py-px rounded-md dark:text-neutral-300 dark:border-neutral-700 dark:bg-neutral-900/70">
149
+ <code class:list={["text-[11px]", codeChipClass]}>
147
150
  {stringLengthLabel}
148
151
  </code>
149
152
  </div>
@@ -8,9 +8,10 @@ import {
8
8
  interface Props {
9
9
  tag?: NavTag;
10
10
  color?: string | ThemeColorByMode;
11
+ class?: string;
11
12
  }
12
13
 
13
- const { tag, color } = Astro.props;
14
+ const { tag, color, class: className } = Astro.props;
14
15
  const config = await getConfig();
15
16
 
16
17
  function getTagText(value: NavTag | undefined): string | undefined {
@@ -51,7 +52,10 @@ const colorStyle = resolvedColor
51
52
  ---
52
53
 
53
54
  <span
54
- class="rd-tag text-[9px] border px-[5px] py-[2px] rounded-full tracking-wide leading-none font-medium shrink-0"
55
+ class:list={[
56
+ "rd-tag text-[9px] border px-[5px] py-[2px] rounded-full tracking-wide leading-none font-medium shrink-0",
57
+ className,
58
+ ]}
55
59
  style={colorStyle}
56
60
  >
57
61
  {text ? text : <slot />}
@@ -64,4 +68,14 @@ const colorStyle = resolvedColor
64
68
  border-color: color-mix(in oklab, var(--rd-tag-color) 0%, transparent);
65
69
  color: color-mix(in oklab, var(--rd-tag-color) 95%, transparent);
66
70
  }
71
+
72
+ :global(.dark) .rd-tag,
73
+ :global([data-theme="dark"]) .rd-tag {
74
+ --rd-tag-color: var(
75
+ --rd-tag-color-dark,
76
+ var(--rd-tag-color-light, var(--color-theme))
77
+ );
78
+ background-color: color-mix(in oklab, var(--rd-tag-color) 12%, transparent);
79
+ color: color-mix(in oklab, var(--rd-tag-color) 85%, transparent);
80
+ }
67
81
  </style>
@@ -44,6 +44,17 @@ export const methodColors: Record<string, string> = {
44
44
  "bg-red-50 dark:bg-red-900 text-red-700/70 dark:text-red-700/10 border-red-700/10",
45
45
  };
46
46
 
47
+ export const methodTagColors: Record<string, string> = {
48
+ get: "#1d4ed8",
49
+ post: "#15803d",
50
+ put: "#c2410c",
51
+ patch: "#a16207",
52
+ delete: "#b91c1c",
53
+ head: "#4338ca",
54
+ options: "#7e22ce",
55
+ trace: "#3f3f46",
56
+ };
57
+
47
58
  export function deriveTitleFromEntryId(filePath: string): string {
48
59
  const filename = path.basename(filePath);
49
60
  const raw = filename.replace(/\.(md|mdx)$/i, "");