radiant-docs 0.1.47 → 0.1.49

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.47",
3
+ "version": "0.1.49",
4
4
  "description": "CLI tool for previewing Radiant documentation locally",
5
5
  "type": "module",
6
6
  "bin": {
@@ -64,7 +64,7 @@ const darkLogoContainerStyle = `padding-top: ${darkLogo.paddingTop}px; padding-b
64
64
 
65
65
  <a
66
66
  href={logoHref}
67
- class="h-full flex items-center justify-center gap-2 lg:gap-3 text-xl font-bold text-neutral-800 dark:text-neutral-100 overflow-hidden"
67
+ class="h-full flex items-center justify-center gap-2 lg:gap-2.5 text-xl font-bold text-neutral-800 dark:text-neutral-100 overflow-hidden"
68
68
  >
69
69
  {
70
70
  lightLogoUrl || darkLogoUrl ? (
@@ -95,7 +95,7 @@ const darkLogoContainerStyle = `padding-top: ${darkLogo.paddingTop}px; padding-b
95
95
  logoPillText && (
96
96
  <span
97
97
  class:list={[
98
- "text-[10px] text-neutral-500 font-semibold bg-neutral-100 dark:bg-neutral-800 px-2 py-px rounded-full border border-neutral-200 dark:border-neutral-700/70 shadow-xs",
98
+ "text-[10px] text-neutral-500 dark:text-neutral-400 font-normal bg-neutral-100 dark:bg-neutral-800 px-2 py-px rounded-full border-[0.5px] border-neutral-900/8 dark:border-neutral-50/8",
99
99
  ]}
100
100
  >
101
101
  {logoPillText}
@@ -869,7 +869,10 @@ const formattedBodyDescription = bodyDescription
869
869
  data-snippet-stack
870
870
  class="relative flex min-h-0 w-full min-w-0 flex-col gap-6 overflow-hidden"
871
871
  >
872
- <div data-snippet-slot class="min-h-0 min-w-0 overflow-hidden">
872
+ <div
873
+ data-snippet-slot
874
+ class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
875
+ >
873
876
  <RequestSnippets
874
877
  api={api}
875
878
  method={route.openApiMethod}
@@ -878,7 +881,10 @@ const formattedBodyDescription = bodyDescription
878
881
  </div>
879
882
  {
880
883
  responses && (
881
- <div data-snippet-slot class="min-h-0 min-w-0 overflow-hidden">
884
+ <div
885
+ data-snippet-slot
886
+ class="min-h-0 min-w-0 overflow-hidden transition-[height,max-height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)]"
887
+ >
882
888
  <ResponseSnippets responses={responses} />
883
889
  </div>
884
890
  )
@@ -1087,14 +1093,37 @@ const formattedBodyDescription = bodyDescription
1087
1093
  stack.querySelectorAll("[data-snippet-slot]"),
1088
1094
  ).filter((slot) => slot instanceof HTMLElement);
1089
1095
 
1090
- const applySlotHeights = () => {
1096
+ const prefersReducedMotion =
1097
+ typeof window.matchMedia === "function" &&
1098
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1099
+
1100
+ const slotHeightTransitionMs = 360;
1101
+ let isAnimatingSlotHeights = false;
1102
+ let slotHeightAnimationTimeoutId = null;
1103
+
1104
+ const applySlotHeights = ({ animate = false } = {}) => {
1091
1105
  if (!slots.length) return;
1106
+ if (isAnimatingSlotHeights && !animate) return;
1107
+
1108
+ const shouldAnimate = animate && !prefersReducedMotion;
1109
+ const previousHeights = shouldAnimate
1110
+ ? slots.map((slot) => Math.ceil(slot.getBoundingClientRect().height))
1111
+ : [];
1112
+ const previousTransitions = shouldAnimate
1113
+ ? slots.map((slot) => slot.style.transition)
1114
+ : [];
1092
1115
 
1093
1116
  const viewportBudget = getViewportBudget();
1094
1117
  if (host instanceof HTMLElement && viewportBudget > 0) {
1095
1118
  host.style.maxHeight = `${viewportBudget}px`;
1096
1119
  }
1097
1120
 
1121
+ if (shouldAnimate) {
1122
+ for (const slot of slots) {
1123
+ slot.style.transition = "none";
1124
+ }
1125
+ }
1126
+
1098
1127
  for (const slot of slots) {
1099
1128
  slot.style.height = "auto";
1100
1129
  slot.style.maxHeight = "none";
@@ -1132,89 +1161,117 @@ const formattedBodyDescription = bodyDescription
1132
1161
  0,
1133
1162
  );
1134
1163
 
1135
- if (naturalTotal <= available) {
1136
- for (let i = 0; i < slots.length; i += 1) {
1137
- const height = naturalHeights[i];
1138
- slots[i].style.height = `${height}px`;
1139
- slots[i].style.maxHeight = `${height}px`;
1140
- }
1141
- return;
1142
- }
1164
+ let targetHeights = naturalHeights;
1143
1165
 
1144
- const roundedHeights = new Array(slots.length).fill(0);
1145
- let remainingHeight = available;
1146
- let remainingIndexes = slots
1147
- .map((_, index) => index)
1148
- .sort((a, b) => naturalHeights[a] - naturalHeights[b]);
1149
-
1150
- while (remainingIndexes.length > 0) {
1151
- const evenShare = remainingHeight / remainingIndexes.length;
1152
- const smallestIndex = remainingIndexes[0];
1153
- const smallestNatural = naturalHeights[smallestIndex];
1154
-
1155
- if (smallestNatural <= evenShare) {
1156
- const fixedHeight = Math.min(smallestNatural, remainingHeight);
1157
- roundedHeights[smallestIndex] = Math.floor(fixedHeight);
1158
- remainingHeight -= roundedHeights[smallestIndex];
1159
- remainingIndexes = remainingIndexes.slice(1);
1160
- continue;
1161
- }
1166
+ if (naturalTotal > available) {
1167
+ targetHeights = new Array(slots.length).fill(0);
1168
+ let remainingHeight = available;
1169
+ let remainingIndexes = slots
1170
+ .map((_, index) => index)
1171
+ .sort((a, b) => naturalHeights[a] - naturalHeights[b]);
1172
+
1173
+ while (remainingIndexes.length > 0) {
1174
+ const evenShare = remainingHeight / remainingIndexes.length;
1175
+ const smallestIndex = remainingIndexes[0];
1176
+ const smallestNatural = naturalHeights[smallestIndex];
1177
+
1178
+ if (smallestNatural <= evenShare) {
1179
+ const fixedHeight = Math.min(smallestNatural, remainingHeight);
1180
+ targetHeights[smallestIndex] = Math.floor(fixedHeight);
1181
+ remainingHeight -= targetHeights[smallestIndex];
1182
+ remainingIndexes = remainingIndexes.slice(1);
1183
+ continue;
1184
+ }
1162
1185
 
1163
- const base = Math.floor(evenShare);
1164
- const remainder = remainingHeight - base * remainingIndexes.length;
1165
- for (let i = 0; i < remainingIndexes.length; i += 1) {
1166
- const index = remainingIndexes[i];
1167
- roundedHeights[index] = base + (i < remainder ? 1 : 0);
1186
+ const base = Math.floor(evenShare);
1187
+ const remainder = remainingHeight - base * remainingIndexes.length;
1188
+ for (let i = 0; i < remainingIndexes.length; i += 1) {
1189
+ const index = remainingIndexes[i];
1190
+ targetHeights[index] = base + (i < remainder ? 1 : 0);
1191
+ }
1192
+ remainingHeight = 0;
1193
+ remainingIndexes = [];
1168
1194
  }
1169
- remainingHeight = 0;
1170
- remainingIndexes = [];
1171
- }
1172
1195
 
1173
- let roundedTotal = roundedHeights.reduce((sum, value) => sum + value, 0);
1174
- let delta = available - roundedTotal;
1196
+ let roundedTotal = targetHeights.reduce((sum, value) => sum + value, 0);
1197
+ let delta = available - roundedTotal;
1175
1198
 
1176
- if (delta !== 0) {
1177
- const adjustOrder = slots
1178
- .map((_, index) => index)
1179
- .sort((a, b) => roundedHeights[b] - roundedHeights[a]);
1199
+ if (delta !== 0) {
1200
+ const adjustOrder = slots
1201
+ .map((_, index) => index)
1202
+ .sort((a, b) => targetHeights[b] - targetHeights[a]);
1180
1203
 
1181
- while (delta !== 0) {
1182
- let changed = false;
1204
+ while (delta !== 0) {
1205
+ let changed = false;
1183
1206
 
1184
- for (const index of adjustOrder) {
1185
- if (delta === 0) break;
1207
+ for (const index of adjustOrder) {
1208
+ if (delta === 0) break;
1186
1209
 
1187
- if (delta > 0) {
1188
- roundedHeights[index] += 1;
1189
- delta -= 1;
1190
- changed = true;
1191
- continue;
1192
- }
1210
+ if (delta > 0) {
1211
+ targetHeights[index] += 1;
1212
+ delta -= 1;
1213
+ changed = true;
1214
+ continue;
1215
+ }
1193
1216
 
1194
- if (roundedHeights[index] > 0) {
1195
- roundedHeights[index] -= 1;
1196
- delta += 1;
1197
- changed = true;
1217
+ if (targetHeights[index] > 0) {
1218
+ targetHeights[index] -= 1;
1219
+ delta += 1;
1220
+ changed = true;
1221
+ }
1198
1222
  }
1223
+
1224
+ if (!changed) break;
1199
1225
  }
1226
+ }
1227
+ }
1200
1228
 
1201
- if (!changed) break;
1229
+ const setSlotHeights = () => {
1230
+ for (let i = 0; i < slots.length; i += 1) {
1231
+ const height = targetHeights[i];
1232
+ slots[i].style.height = `${height}px`;
1233
+ slots[i].style.maxHeight = `${height}px`;
1202
1234
  }
1235
+ };
1236
+
1237
+ if (!shouldAnimate) {
1238
+ setSlotHeights();
1239
+ return;
1240
+ }
1241
+
1242
+ isAnimatingSlotHeights = true;
1243
+ if (slotHeightAnimationTimeoutId) {
1244
+ window.clearTimeout(slotHeightAnimationTimeoutId);
1203
1245
  }
1204
1246
 
1205
1247
  for (let i = 0; i < slots.length; i += 1) {
1206
- slots[i].style.height = `${roundedHeights[i]}px`;
1207
- slots[i].style.maxHeight = `${roundedHeights[i]}px`;
1248
+ const height = previousHeights[i];
1249
+ slots[i].style.height = `${height}px`;
1250
+ slots[i].style.maxHeight = `${height}px`;
1208
1251
  }
1252
+
1253
+ void stack.offsetHeight;
1254
+
1255
+ for (let i = 0; i < slots.length; i += 1) {
1256
+ slots[i].style.transition = previousTransitions[i];
1257
+ }
1258
+
1259
+ window.requestAnimationFrame(setSlotHeights);
1260
+
1261
+ slotHeightAnimationTimeoutId = window.setTimeout(() => {
1262
+ isAnimatingSlotHeights = false;
1263
+ slotHeightAnimationTimeoutId = null;
1264
+ applySlotHeights();
1265
+ }, slotHeightTransitionMs + 80);
1209
1266
  };
1210
1267
 
1211
- const scheduleApplySlotHeights = () => {
1268
+ const scheduleApplySlotHeights = (options = {}) => {
1212
1269
  window.requestAnimationFrame(() => {
1213
- window.requestAnimationFrame(applySlotHeights);
1270
+ window.requestAnimationFrame(() => applySlotHeights(options));
1214
1271
  });
1215
1272
  };
1216
1273
 
1217
- const resizeObserver = new ResizeObserver(applySlotHeights);
1274
+ const resizeObserver = new ResizeObserver(() => applySlotHeights());
1218
1275
  resizeObserver.observe(stack);
1219
1276
  if (host instanceof HTMLElement) {
1220
1277
  resizeObserver.observe(host);
@@ -1230,7 +1287,7 @@ const formattedBodyDescription = bodyDescription
1230
1287
  });
1231
1288
  window.addEventListener(
1232
1289
  "rd:snippet-content-change",
1233
- scheduleApplySlotHeights,
1290
+ () => scheduleApplySlotHeights({ animate: true }),
1234
1291
  );
1235
1292
  applySlotHeights();
1236
1293
  }
@@ -24,7 +24,7 @@ function buildHref(route: Route): string {
24
24
  {
25
25
  (previousRoute || nextRoute) && (
26
26
  <nav class="w-full pt-16" aria-label="Page pagination">
27
- <div class="flex gap-4 xs:grid-cols-2">
27
+ <div class="flex gap-4 flex-col sm:flex-row">
28
28
  {previousRoute && (
29
29
  <a
30
30
  href={buildHref(previousRoute)}
@@ -106,7 +106,7 @@ const containsActivePage = item.pages.some((child) => {
106
106
  x-show="expanded"
107
107
  x-collapse
108
108
  x-cloak
109
- class="border-l border-neutral-300/80 ml-[15.5px] pl-2"
109
+ class="border-l border-neutral-900/12 dark:border-neutral-100/12 ml-[15.5px] pl-2"
110
110
  >
111
111
  {
112
112
  item.pages.map((child) => (
@@ -321,14 +321,52 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
321
321
  pillLeft: 0,
322
322
  pillWidth: 0,
323
323
  pillVisible: false,
324
+ previousSelected: null,
325
+ isTransitioning: false,
326
+ isManagedSlot: false,
327
+ transitionDirection: 1,
328
+ transitionDurationMs: 400,
329
+ transitionEasing: "cubic-bezier(0.22, 1, 0.36, 1)",
324
330
  tabSyncHandler: null,
331
+ transitionTimeoutId: null,
325
332
  copyTimeoutId: null,
333
+ readMotionTokens() {
334
+ const styles = window.getComputedStyle(document.documentElement);
335
+ const configuredDurationMs = Number.parseFloat(
336
+ styles.getPropertyValue("--rd-panel-transition-duration-ms"),
337
+ );
338
+ this.transitionDurationMs = Number.isFinite(configuredDurationMs)
339
+ ? configuredDurationMs
340
+ : this.transitionDurationMs;
341
+ this.transitionEasing =
342
+ styles.getPropertyValue("--rd-panel-transition-easing").trim() ||
343
+ this.transitionEasing;
344
+ },
326
345
  init() {
327
- if (!this.hasMultiple) return;
328
- const sync = () => this.syncPill();
346
+ this.isManagedSlot = !!this.$root?.closest("[data-snippet-slot]");
347
+ this.readMotionTokens();
348
+ if (
349
+ typeof window.matchMedia === "function" &&
350
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
351
+ ) {
352
+ this.transitionDurationMs = 0;
353
+ }
354
+
355
+ const sync = () => {
356
+ this.syncPill();
357
+ this.syncSnippetHeight();
358
+ };
329
359
  this.tabSyncHandler = sync;
330
360
  this.$nextTick(() => {
361
+ if (this.$refs.snippetPanels) {
362
+ this.$refs.snippetPanels.style.transitionDuration =
363
+ this.transitionDurationMs + "ms";
364
+ this.$refs.snippetPanels.style.transitionTimingFunction =
365
+ this.transitionEasing;
366
+ }
331
367
  this.syncPill();
368
+ this.syncSnippetHeight();
369
+ if (!this.hasMultiple) return;
332
370
  this.$refs.tabList?.addEventListener("scroll", sync, {
333
371
  passive: true,
334
372
  });
@@ -352,13 +390,96 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
352
390
  this.pillWidth = activeRect.width;
353
391
  this.pillVisible = true;
354
392
  },
393
+ getPanelHeight(panel) {
394
+ if (!panel) return 0;
395
+ const child = panel.firstElementChild;
396
+ const childHeight =
397
+ child instanceof HTMLElement ? child.scrollHeight : 0;
398
+ return Math.ceil(
399
+ Math.max(panel.scrollHeight, childHeight, panel.getBoundingClientRect().height),
400
+ );
401
+ },
402
+ syncSnippetHeight() {
403
+ const panels = this.$refs.snippetPanels;
404
+ if (!panels) return;
405
+ if (this.isManagedSlot) {
406
+ panels.style.height = "";
407
+ return;
408
+ }
409
+ const activePanel = panels.querySelector(
410
+ '[data-snippet-index="' + this.selected + '"]',
411
+ );
412
+ const activeHeight = this.getPanelHeight(activePanel);
413
+ if (activeHeight > 0) {
414
+ panels.style.height = activeHeight + "px";
415
+ }
416
+ },
417
+ finishTransition() {
418
+ this.isTransitioning = false;
419
+ this.previousSelected = null;
420
+ this.transitionTimeoutId = null;
421
+ this.$nextTick(() => {
422
+ this.syncSnippetHeight();
423
+ window.dispatchEvent(new CustomEvent("rd:snippet-content-change"));
424
+ });
425
+ },
355
426
  select(index) {
427
+ if (index === this.selected) return;
428
+
429
+ if (this.transitionTimeoutId) {
430
+ window.clearTimeout(this.transitionTimeoutId);
431
+ this.transitionTimeoutId = null;
432
+ this.isTransitioning = false;
433
+ this.previousSelected = null;
434
+ }
435
+
436
+ const previousIndex = this.selected;
437
+ this.previousSelected = previousIndex;
438
+ this.transitionDirection = index > previousIndex ? 1 : -1;
356
439
  this.selected = index;
440
+ this.isTransitioning = true;
357
441
  this.$nextTick(() => {
358
442
  this.syncPill();
443
+ this.syncSnippetHeight();
359
444
  window.dispatchEvent(new CustomEvent("rd:snippet-content-change"));
445
+
446
+ if (this.transitionDurationMs === 0) {
447
+ this.finishTransition();
448
+ return;
449
+ }
450
+
451
+ this.transitionTimeoutId = window.setTimeout(() => {
452
+ this.finishTransition();
453
+ }, this.transitionDurationMs);
360
454
  });
361
455
  },
456
+ getPanelStyle(index) {
457
+ const base = "position: absolute; top: 0; right: 0; left: 0;";
458
+ const isActive = index === this.selected;
459
+ const isPrevious = this.isTransitioning && index === this.previousSelected;
460
+
461
+ if (!isActive && !isPrevious) {
462
+ return "display: none; opacity: 0; pointer-events: none; visibility: hidden; z-index: 0;";
463
+ }
464
+
465
+ if (!this.isTransitioning) {
466
+ return "position: relative; transform: translateX(0); opacity: 1; pointer-events: auto; visibility: visible; z-index: 1;";
467
+ }
468
+
469
+ if (isActive) {
470
+ const animationName =
471
+ this.transitionDirection === 1
472
+ ? "rd-snippet-slide-in-from-right"
473
+ : "rd-snippet-slide-in-from-left";
474
+ return "position: relative; opacity: 1; pointer-events: auto; visibility: visible; z-index: 2; animation: " + animationName + " " + this.transitionDurationMs + "ms " + this.transitionEasing + " both;";
475
+ }
476
+
477
+ const animationName =
478
+ this.transitionDirection === 1
479
+ ? "rd-snippet-slide-out-to-left"
480
+ : "rd-snippet-slide-out-to-right";
481
+ return base + " opacity: 1; pointer-events: none; visibility: visible; z-index: 1; animation: " + animationName + " " + this.transitionDurationMs + "ms " + this.transitionEasing + " both;";
482
+ },
362
483
  async copySelected() {
363
484
  const snippet = this.snippets[this.selected];
364
485
  if (!snippet) return;
@@ -383,21 +504,21 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
383
504
  >
384
505
  <div class="group/prose-code not-prose relative h-full min-h-0 w-full max-w-full min-w-0">
385
506
  <div
386
- class="flex h-full min-h-0 w-full max-w-full min-w-0 flex-col overflow-hidden rounded-xl border border-neutral-200 bg-white shadow-xs dark:border-neutral-800 dark:bg-(--rd-code-surface)"
507
+ class="flex h-full min-h-0 w-full max-w-full min-w-0 flex-col rounded-xl"
387
508
  >
388
509
  <div
389
- class="flex items-center justify-between gap-2 border-b border-neutral-200 bg-neutral-50 rounded-t-xl inset-shadow-sm inset-shadow-neutral-100/80 dark:border-neutral-800 dark:bg-neutral-900/60 dark:inset-shadow-neutral-900/80"
510
+ class="flex items-center justify-between gap-2"
390
511
  >
391
512
  {
392
513
  hasMultipleRequests ? (
393
514
  <div class="min-w-0 flex-1 overflow-hidden rounded-t-xl">
394
515
  <div
395
516
  x-ref="tabList"
396
- class="relative flex min-w-0 items-end gap-1 overflow-x-auto pl-1 pr-8 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
517
+ class="relative flex min-w-0 items-end gap-1 overflow-x-auto overscroll-x-contain pl-1 pr-8 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
397
518
  >
398
519
  <div
399
520
  aria-hidden="true"
400
- class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-lg border-[0.5px] border-neutral-200 bg-white shadow-xs transition-[left,width,opacity] duration-200 ease-out dark:border-neutral-700/70 dark:bg-(--rd-code-surface)"
521
+ class="pointer-events-none absolute top-0 z-0 h-[28px] rounded-[9px] border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface) transition-[left,width,opacity] duration-200 ease-out"
401
522
  x-bind:class="pillVisible ? 'opacity-100' : 'opacity-0'"
402
523
  x-bind:style="'left:' + pillLeft + 'px;width:' + pillWidth + 'px;'"
403
524
  />
@@ -410,28 +531,30 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
410
531
  type="button"
411
532
  x-bind:data-rd-snippet-tab="index"
412
533
  x-on:click="select(index)"
413
- class="relative z-10 inline-flex h-9 items-center gap-2 border-0 bg-transparent px-3 py-1.5 text-xs font-medium transition-colors duration-150 focus:outline-none focus-visible:outline-none cursor-pointer"
534
+ class="relative z-10 inline-flex h-9 items-start border-0 bg-transparent px-3 py-0 text-xs font-medium transition-colors duration-150 focus:outline-none focus-visible:outline-none cursor-pointer"
414
535
  x-bind:class="selected === index ? 'text-foreground' : 'text-muted-foreground'"
415
536
  >
416
- <span
417
- x-show="snippet.iconSvg"
418
- x-html="snippet.iconSvg"
419
- class="pointer-events-none inline-flex size-3.5 shrink-0 items-center rounded-[4px] transition-opacity duration-150"
420
- x-bind:class="selected === index ? 'opacity-100' : 'opacity-70'"></span>
421
- <span class="whitespace-pre leading-none" x-text="snippet.label"></span>
537
+ <span class="pointer-events-none inline-flex h-[28px] items-center gap-2">
538
+ <span
539
+ x-show="snippet.iconSvg"
540
+ x-html="snippet.iconSvg"
541
+ class="inline-flex size-3.5 shrink-0 items-center rounded-[4px] transition-opacity duration-150"
542
+ x-bind:class="selected === index ? 'opacity-100' : 'opacity-70'"></span>
543
+ <span class="whitespace-pre leading-none" x-text="snippet.label"></span>
544
+ </span>
422
545
  </button>
423
546
  </template>
424
547
  </div>
425
548
  </div>
426
549
  ) : (
427
550
  <div class="min-w-0 flex-1">
428
- <div class="relative h-9 w-fit max-w-full rounded-tl-xl bg-white dark:bg-(--rd-code-surface)">
429
- <div class="absolute inset-x-0 -bottom-px h-px bg-white dark:bg-(--rd-code-surface)"></div>
551
+ <div class="relative h-9 w-fit max-w-full rounded-tl-xl bg-(--rd-code-surface)">
552
+ <div class="absolute inset-x-0 -bottom-px h-px bg-(--rd-code-surface)"></div>
430
553
  <CodeTabEdge
431
- className="pointer-events-none absolute -top-px left-full z-10 h-[calc(100%+2px)]"
554
+ className="pointer-events-none absolute -top-[0.5px] left-full z-10 h-[calc(100%+1.5px)]"
432
555
  />
433
556
 
434
- <div class="relative z-20 inline-flex h-9 max-w-full items-center gap-2 pl-5 pr-3 py-1.5 text-xs font-medium text-neutral-700 dark:text-neutral-300">
557
+ <div class="relative z-20 inline-flex h-9 max-w-full items-center gap-2 rounded-tl-xl border-t-[0.5px] border-l-[0.5px] border-(--rd-code-tab-edge-border) pl-5 pr-3 py-1.5 text-xs font-medium text-neutral-700 dark:text-neutral-300">
435
558
  <span
436
559
  class="size-3.5 shrink-0 self-center rounded-[4px]"
437
560
  set:html={firstSnippet.iconSvg}
@@ -443,8 +566,8 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
443
566
  )
444
567
  }
445
568
 
446
- <div class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-white dark:bg-(--rd-code-surface)">
447
- <div class="absolute inset-x-0 -bottom-px h-px bg-white dark:bg-(--rd-code-surface)"></div>
569
+ <div class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-(--rd-code-surface) border-t-[0.5px] border-r-[0.5px] border-(--rd-code-tab-edge-border)">
570
+ <div class="absolute inset-x-0 -bottom-px h-px bg-(--rd-code-surface)"></div>
448
571
  <CodeTabEdge
449
572
  className="pointer-events-none absolute -top-px right-full z-10 h-[calc(100%+2px)] rotate-y-180"
450
573
  />
@@ -470,14 +593,22 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
470
593
  </div>
471
594
  </div>
472
595
 
473
- <div class="relative min-h-0 min-w-0 flex-1 overflow-hidden rounded-b-xl">
474
- <div class="relative h-full overflow-auto [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">
475
- <div class="pointer-events-none sticky top-0 z-10 -mb-4 h-4 w-full bg-linear-to-b from-white to-transparent dark:from-(--rd-code-surface)"></div>
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)">
597
+ <div
598
+ x-ref="snippetPanels"
599
+ 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"
600
+ style="transition-duration: var(--rd-panel-transition-duration); transition-timing-function: var(--rd-panel-transition-easing);"
601
+ >
476
602
  {
477
603
  requestSnippetItems.map((snippet, index) => (
478
604
  <pre
479
- class="relative m-0 min-w-full bg-white p-0 text-[13px] leading-6 dark:bg-(--rd-code-surface)"
480
- x-show={`selected === ${index}`}
605
+ class="relative m-0 min-w-full p-0 text-[13px] leading-6"
606
+ x-bind:style={`getPanelStyle(${index})`}
607
+ style={
608
+ index === 0
609
+ ? "position: relative;"
610
+ : "display: none; position: absolute; top: 0; right: 0; left: 0; opacity: 0; visibility: hidden; pointer-events: none;"
611
+ }
481
612
  {...(index !== 0 ? { "x-cloak": true } : {})}
482
613
  data-snippet-index={index}
483
614
  ><code data-rd-code-theme class="block min-w-full py-2.5 font-mono text-neutral-800 dark:text-neutral-200"><Fragment set:html={snippet.renderedCodeLinesHtml} /></code></pre>
@@ -488,3 +619,49 @@ const hasMultipleRequests = requestSnippetItems.length > 1;
488
619
  </div>
489
620
  </div>
490
621
  </div>
622
+
623
+ <style>
624
+ @keyframes rd-snippet-slide-in-from-right {
625
+ from {
626
+ transform: translateX(100%);
627
+ opacity: 0;
628
+ }
629
+ to {
630
+ transform: translateX(0);
631
+ opacity: 1;
632
+ }
633
+ }
634
+
635
+ @keyframes rd-snippet-slide-in-from-left {
636
+ from {
637
+ transform: translateX(-100%);
638
+ opacity: 0;
639
+ }
640
+ to {
641
+ transform: translateX(0);
642
+ opacity: 1;
643
+ }
644
+ }
645
+
646
+ @keyframes rd-snippet-slide-out-to-left {
647
+ from {
648
+ transform: translateX(0);
649
+ opacity: 1;
650
+ }
651
+ to {
652
+ transform: translateX(-100%);
653
+ opacity: 0;
654
+ }
655
+ }
656
+
657
+ @keyframes rd-snippet-slide-out-to-right {
658
+ from {
659
+ transform: translateX(0);
660
+ opacity: 1;
661
+ }
662
+ to {
663
+ transform: translateX(100%);
664
+ opacity: 0;
665
+ }
666
+ }
667
+ </style>