raffel 1.1.49 → 1.1.51-next.ce68de0

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.
@@ -684,6 +684,16 @@
684
684
  gap: 0;
685
685
  }
686
686
 
687
+ /* Drop the (empty) TOC gutter on endpoint views so the operation's
688
+ two-column content — docs + samples panel — can use the full width. */
689
+ .main-shell.main-shell-no-toc {
690
+ grid-template-columns: minmax(0, 1fr);
691
+ }
692
+
693
+ .main-shell.main-shell-no-toc .toc {
694
+ display: none;
695
+ }
696
+
687
697
  /* ========== SIDEBAR ========== */
688
698
  .sidebar {
689
699
  background: var(--sidebar-bg);
@@ -3535,6 +3545,39 @@
3535
3545
 
3536
3546
  .http-code-copy:hover { color: var(--text-color); border-color: var(--text-muted); }
3537
3547
 
3548
+ /* Right panel (third column): keep request/response samples dark & legible
3549
+ regardless of the active theme, matching ReDoc's sample column. */
3550
+ .endpoint-right .endpoint-right-section + .endpoint-right-section {
3551
+ margin-top: 24px;
3552
+ }
3553
+
3554
+ .endpoint-right .http-code-samples {
3555
+ background: rgba(0,0,0,0.25);
3556
+ border-color: rgba(255,255,255,0.1);
3557
+ }
3558
+
3559
+ .endpoint-right .http-code-samples .code-tabs {
3560
+ background: rgba(0,0,0,0.2);
3561
+ border-color: rgba(255,255,255,0.1);
3562
+ }
3563
+
3564
+ .endpoint-right .http-code-sample-pre {
3565
+ color: var(--code-panel-text);
3566
+ }
3567
+
3568
+ .endpoint-right .code-tab {
3569
+ color: #90a4ae;
3570
+ }
3571
+
3572
+ .endpoint-right .code-tab.active {
3573
+ background: rgba(255,255,255,0.12);
3574
+ color: #fff;
3575
+ }
3576
+
3577
+ .endpoint-right .response-subhead {
3578
+ color: #90a4ae;
3579
+ }
3580
+
3538
3581
  /* ========== TRY IT OUT ========== */
3539
3582
  .try-it-out {
3540
3583
  background: rgba(0,0,0,0.2);
@@ -1385,20 +1385,76 @@ function renderCodeExamples(endpoint, data) {
1385
1385
  return wrap;
1386
1386
  }
1387
1387
  // Append a JSON example block to a container.
1388
- function appendJsonExample(container, value, label = 'Example') {
1389
- if (value === null || value === undefined)
1390
- return;
1391
- const head = doc.createElement('div');
1392
- head.className = 'response-subhead';
1393
- head.style.marginTop = '12px';
1394
- head.textContent = label;
1395
- container.appendChild(head);
1396
- const pre = doc.createElement('pre');
1397
- pre.className = 'http-code-sample-pre';
1398
- pre.style.border = '1px solid var(--border-color)';
1399
- pre.style.borderRadius = '8px';
1400
- pre.textContent = JSON.stringify(value, null, 2);
1401
- container.appendChild(pre);
1388
+ // Syntax-highlight a JSON value into HTML using the .sample-json token classes.
1389
+ function highlightJsonHtml(value) {
1390
+ let json = JSON.stringify(value, null, 2);
1391
+ if (json === undefined)
1392
+ return '';
1393
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1394
+ return json.replace(/("(?:\\.|[^"\\])*"(\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, (match, _token, colon) => {
1395
+ let cls = 'json-number';
1396
+ if (match[0] === '"')
1397
+ cls = colon ? 'json-key' : 'json-string';
1398
+ else if (match === 'true' || match === 'false')
1399
+ cls = 'json-boolean';
1400
+ else if (match === 'null')
1401
+ cls = 'json-null';
1402
+ return `<span class="${cls}">${match}</span>`;
1403
+ });
1404
+ }
1405
+ // Right-panel response samples: one status tab per response, each showing the
1406
+ // generated JSON example (colorized) — ReDoc's response sample switcher.
1407
+ function renderResponseSamples(responses) {
1408
+ const wrap = doc.createElement('div');
1409
+ const tabs = doc.createElement('div');
1410
+ tabs.className = 'sample-tabs';
1411
+ const contents = doc.createElement('div');
1412
+ contents.className = 'sample-contents';
1413
+ const entries = Object.entries(responses);
1414
+ entries.forEach(([status, resp], index) => {
1415
+ const statusClass = status.startsWith('2') ? 'status-2xx'
1416
+ : status.startsWith('4') ? 'status-4xx'
1417
+ : status.startsWith('5') ? 'status-5xx'
1418
+ : '';
1419
+ const tab = doc.createElement('button');
1420
+ tab.type = 'button';
1421
+ tab.className = `sample-tab ${statusClass}${index === 0 ? ' active' : ''}`;
1422
+ tab.textContent = status;
1423
+ const content = doc.createElement('div');
1424
+ content.className = `sample-content${index === 0 ? ' active' : ''}`;
1425
+ const ct = resp.content ? Object.keys(resp.content)[0] : null;
1426
+ const schema = ct ? resp.content[ct]?.schema : null;
1427
+ const example = schema ? generateExampleFromSchema(schema) : null;
1428
+ if (example !== null && example !== undefined) {
1429
+ if (ct) {
1430
+ const typeLine = doc.createElement('div');
1431
+ typeLine.className = 'sample-content-type';
1432
+ typeLine.textContent = ct;
1433
+ content.appendChild(typeLine);
1434
+ }
1435
+ const pre = doc.createElement('pre');
1436
+ pre.className = 'sample-json';
1437
+ pre.innerHTML = highlightJsonHtml(example);
1438
+ content.appendChild(pre);
1439
+ }
1440
+ else {
1441
+ const empty = doc.createElement('div');
1442
+ empty.className = 'no-example';
1443
+ empty.textContent = resp.description || 'No response body.';
1444
+ content.appendChild(empty);
1445
+ }
1446
+ tab.onclick = () => {
1447
+ tabs.querySelectorAll('.sample-tab').forEach((t) => t.classList.remove('active'));
1448
+ contents.querySelectorAll('.sample-content').forEach((c) => c.classList.remove('active'));
1449
+ tab.classList.add('active');
1450
+ content.classList.add('active');
1451
+ };
1452
+ tabs.appendChild(tab);
1453
+ contents.appendChild(content);
1454
+ });
1455
+ wrap.appendChild(tabs);
1456
+ wrap.appendChild(contents);
1457
+ return wrap;
1402
1458
  }
1403
1459
  function renderSchemaTree(parent, schema, depth = 0) {
1404
1460
  if (!schema)
@@ -1541,7 +1597,6 @@ function renderResponseAccordion(status, resp, openByDefault) {
1541
1597
  sub.textContent = `Response Body${contentType ? ` · ${contentType}` : ''}`;
1542
1598
  block.appendChild(sub);
1543
1599
  renderSchemaTree(block, schema);
1544
- appendJsonExample(block, generateExampleFromSchema(schema));
1545
1600
  body.appendChild(block);
1546
1601
  }
1547
1602
  if (!body.children.length) {
@@ -1562,6 +1617,15 @@ function renderEndpointDetails(endpoint) {
1562
1617
  appendProtocolConsole(container, { doc, spec, wsSpec, streamsSpec, jsonrpcSpec, activeProtocol, endpoint, data, esc, escapeAttr });
1563
1618
  const appendMany = (items) => items.forEach(([title, value]) => appendSchemaSubsection(container, title, value));
1564
1619
  if (activeProtocol === 'http') {
1620
+ // Two-column operation layout (ReDoc-style): the left column carries the
1621
+ // contract (params + schemas + responses); the right column is a sticky
1622
+ // dark panel with the request samples and response examples.
1623
+ const content = doc.createElement('div');
1624
+ content.className = 'endpoint-content';
1625
+ const left = doc.createElement('div');
1626
+ left.className = 'endpoint-left';
1627
+ const right = doc.createElement('div');
1628
+ right.className = 'endpoint-right';
1565
1629
  const params = (data.parameters ?? []);
1566
1630
  if (params.length > 0) {
1567
1631
  const section = doc.createElement('div');
@@ -1578,7 +1642,7 @@ function renderEndpointDetails(endpoint) {
1578
1642
  if (inGroup.length > 0)
1579
1643
  section.appendChild(renderParamGroup(title, inGroup));
1580
1644
  });
1581
- container.appendChild(section);
1645
+ left.appendChild(section);
1582
1646
  }
1583
1647
  const reqBody = data.requestBody;
1584
1648
  if (reqBody?.content) {
@@ -1586,12 +1650,10 @@ function renderEndpointDetails(endpoint) {
1586
1650
  section.className = 'endpoint-subsection';
1587
1651
  const contentType = Object.keys(reqBody.content)[0];
1588
1652
  section.innerHTML = `<div class="subsection-label">REQUEST BODY${reqBody.required ? ' <span style="color:#ef4444">required</span>' : ''}${contentType ? ` · ${esc(contentType)}` : ''}</div>`;
1589
- const content = reqBody.content[contentType];
1590
- if (content?.schema) {
1591
- renderSchemaTree(section, content.schema);
1592
- appendJsonExample(section, generateExampleFromSchema(content.schema));
1593
- }
1594
- container.appendChild(section);
1653
+ const bodyContent = reqBody.content[contentType];
1654
+ if (bodyContent?.schema)
1655
+ renderSchemaTree(section, bodyContent.schema);
1656
+ left.appendChild(section);
1595
1657
  }
1596
1658
  const responses = data.responses;
1597
1659
  if (responses && Object.keys(responses).length > 0) {
@@ -1611,14 +1673,25 @@ function renderEndpointDetails(endpoint) {
1611
1673
  if (first)
1612
1674
  first.classList.add('open');
1613
1675
  }
1614
- container.appendChild(section);
1676
+ left.appendChild(section);
1677
+ }
1678
+ // Right column: request samples (cURL / JavaScript / Python / Rust).
1679
+ const samplesSection = doc.createElement('div');
1680
+ samplesSection.className = 'endpoint-right-section';
1681
+ samplesSection.innerHTML = '<div class="endpoint-right-header">Request samples</div>';
1682
+ samplesSection.appendChild(renderCodeExamples(endpoint, data));
1683
+ right.appendChild(samplesSection);
1684
+ // Right column: response samples (JSON examples per status code).
1685
+ if (responses && Object.keys(responses).length > 0) {
1686
+ const respSamples = doc.createElement('div');
1687
+ respSamples.className = 'endpoint-right-section';
1688
+ respSamples.innerHTML = '<div class="endpoint-right-header">Response samples</div>';
1689
+ respSamples.appendChild(renderResponseSamples(responses));
1690
+ right.appendChild(respSamples);
1615
1691
  }
1616
- // Request samples (cURL / JavaScript / Python / Rust)
1617
- const examplesSection = doc.createElement('div');
1618
- examplesSection.className = 'endpoint-subsection';
1619
- examplesSection.innerHTML = '<div class="subsection-label">REQUEST SAMPLES</div>';
1620
- examplesSection.appendChild(renderCodeExamples(endpoint, data));
1621
- container.appendChild(examplesSection);
1692
+ content.appendChild(left);
1693
+ content.appendChild(right);
1694
+ container.appendChild(content);
1622
1695
  }
1623
1696
  if (activeProtocol === 'websocket') {
1624
1697
  appendInfoGrid(container, [['Channel Type', data.type], ['Path', endpoint.path]]);
@@ -1901,23 +1974,35 @@ function pageNavCard(entry, label, direction) {
1901
1974
  button.onclick = () => setDocsPage(entry.path);
1902
1975
  return button;
1903
1976
  }
1977
+ function setTocColumn(visible) {
1978
+ const shell = doc.querySelector?.('.main-shell');
1979
+ if (shell)
1980
+ shell.classList.toggle('main-shell-no-toc', !visible);
1981
+ }
1904
1982
  function renderToc(root) {
1905
1983
  const toc = byId('pageToc');
1906
1984
  if (!toc)
1907
1985
  return;
1908
1986
  toc.textContent = '';
1909
- if (tocConfig.enabled === false)
1987
+ if (tocConfig.enabled === false) {
1988
+ setTocColumn(false);
1910
1989
  return;
1911
- if (root.querySelector?.('[data-markdown-ignore-all="true"]'))
1990
+ }
1991
+ if (root.querySelector?.('[data-markdown-ignore-all="true"]')) {
1992
+ setTocColumn(false);
1912
1993
  return;
1994
+ }
1913
1995
  const min = Number(tocConfig.minLevel ?? 2);
1914
1996
  const max = Number(tocConfig.maxLevel ?? 3);
1915
1997
  const headings = Array.from(root.querySelectorAll('h1,h2,h3,h4,h5,h6') ?? []).filter((heading) => {
1916
1998
  const level = Number(heading.tagName.slice(1));
1917
1999
  return heading.id && level >= min && level <= max && heading.dataset?.markdownIgnore !== 'true';
1918
2000
  });
1919
- if (headings.length === 0)
2001
+ if (headings.length === 0) {
2002
+ setTocColumn(false);
1920
2003
  return;
2004
+ }
2005
+ setTocColumn(true);
1921
2006
  const title = doc.createElement('div');
1922
2007
  title.className = 'toc-title';
1923
2008
  title.textContent = 'On this page';
@@ -1385,20 +1385,76 @@ function renderCodeExamples(endpoint, data) {
1385
1385
  return wrap;
1386
1386
  }
1387
1387
  // Append a JSON example block to a container.
1388
- function appendJsonExample(container, value, label = 'Example') {
1389
- if (value === null || value === undefined)
1390
- return;
1391
- const head = doc.createElement('div');
1392
- head.className = 'response-subhead';
1393
- head.style.marginTop = '12px';
1394
- head.textContent = label;
1395
- container.appendChild(head);
1396
- const pre = doc.createElement('pre');
1397
- pre.className = 'http-code-sample-pre';
1398
- pre.style.border = '1px solid var(--border-color)';
1399
- pre.style.borderRadius = '8px';
1400
- pre.textContent = JSON.stringify(value, null, 2);
1401
- container.appendChild(pre);
1388
+ // Syntax-highlight a JSON value into HTML using the .sample-json token classes.
1389
+ function highlightJsonHtml(value) {
1390
+ let json = JSON.stringify(value, null, 2);
1391
+ if (json === undefined)
1392
+ return '';
1393
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1394
+ return json.replace(/("(?:\\.|[^"\\])*"(\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, (match, _token, colon) => {
1395
+ let cls = 'json-number';
1396
+ if (match[0] === '"')
1397
+ cls = colon ? 'json-key' : 'json-string';
1398
+ else if (match === 'true' || match === 'false')
1399
+ cls = 'json-boolean';
1400
+ else if (match === 'null')
1401
+ cls = 'json-null';
1402
+ return `<span class="${cls}">${match}</span>`;
1403
+ });
1404
+ }
1405
+ // Right-panel response samples: one status tab per response, each showing the
1406
+ // generated JSON example (colorized) — ReDoc's response sample switcher.
1407
+ function renderResponseSamples(responses) {
1408
+ const wrap = doc.createElement('div');
1409
+ const tabs = doc.createElement('div');
1410
+ tabs.className = 'sample-tabs';
1411
+ const contents = doc.createElement('div');
1412
+ contents.className = 'sample-contents';
1413
+ const entries = Object.entries(responses);
1414
+ entries.forEach(([status, resp], index) => {
1415
+ const statusClass = status.startsWith('2') ? 'status-2xx'
1416
+ : status.startsWith('4') ? 'status-4xx'
1417
+ : status.startsWith('5') ? 'status-5xx'
1418
+ : '';
1419
+ const tab = doc.createElement('button');
1420
+ tab.type = 'button';
1421
+ tab.className = `sample-tab ${statusClass}${index === 0 ? ' active' : ''}`;
1422
+ tab.textContent = status;
1423
+ const content = doc.createElement('div');
1424
+ content.className = `sample-content${index === 0 ? ' active' : ''}`;
1425
+ const ct = resp.content ? Object.keys(resp.content)[0] : null;
1426
+ const schema = ct ? resp.content[ct]?.schema : null;
1427
+ const example = schema ? generateExampleFromSchema(schema) : null;
1428
+ if (example !== null && example !== undefined) {
1429
+ if (ct) {
1430
+ const typeLine = doc.createElement('div');
1431
+ typeLine.className = 'sample-content-type';
1432
+ typeLine.textContent = ct;
1433
+ content.appendChild(typeLine);
1434
+ }
1435
+ const pre = doc.createElement('pre');
1436
+ pre.className = 'sample-json';
1437
+ pre.innerHTML = highlightJsonHtml(example);
1438
+ content.appendChild(pre);
1439
+ }
1440
+ else {
1441
+ const empty = doc.createElement('div');
1442
+ empty.className = 'no-example';
1443
+ empty.textContent = resp.description || 'No response body.';
1444
+ content.appendChild(empty);
1445
+ }
1446
+ tab.onclick = () => {
1447
+ tabs.querySelectorAll('.sample-tab').forEach((t) => t.classList.remove('active'));
1448
+ contents.querySelectorAll('.sample-content').forEach((c) => c.classList.remove('active'));
1449
+ tab.classList.add('active');
1450
+ content.classList.add('active');
1451
+ };
1452
+ tabs.appendChild(tab);
1453
+ contents.appendChild(content);
1454
+ });
1455
+ wrap.appendChild(tabs);
1456
+ wrap.appendChild(contents);
1457
+ return wrap;
1402
1458
  }
1403
1459
  function renderSchemaTree(parent, schema, depth = 0) {
1404
1460
  if (!schema)
@@ -1541,7 +1597,6 @@ function renderResponseAccordion(status, resp, openByDefault) {
1541
1597
  sub.textContent = `Response Body${contentType ? ` · ${contentType}` : ''}`;
1542
1598
  block.appendChild(sub);
1543
1599
  renderSchemaTree(block, schema);
1544
- appendJsonExample(block, generateExampleFromSchema(schema));
1545
1600
  body.appendChild(block);
1546
1601
  }
1547
1602
  if (!body.children.length) {
@@ -1562,6 +1617,15 @@ function renderEndpointDetails(endpoint) {
1562
1617
  appendProtocolConsole(container, { doc, spec, wsSpec, streamsSpec, jsonrpcSpec, activeProtocol, endpoint, data, esc, escapeAttr });
1563
1618
  const appendMany = (items) => items.forEach(([title, value]) => appendSchemaSubsection(container, title, value));
1564
1619
  if (activeProtocol === 'http') {
1620
+ // Two-column operation layout (ReDoc-style): the left column carries the
1621
+ // contract (params + schemas + responses); the right column is a sticky
1622
+ // dark panel with the request samples and response examples.
1623
+ const content = doc.createElement('div');
1624
+ content.className = 'endpoint-content';
1625
+ const left = doc.createElement('div');
1626
+ left.className = 'endpoint-left';
1627
+ const right = doc.createElement('div');
1628
+ right.className = 'endpoint-right';
1565
1629
  const params = (data.parameters ?? []);
1566
1630
  if (params.length > 0) {
1567
1631
  const section = doc.createElement('div');
@@ -1578,7 +1642,7 @@ function renderEndpointDetails(endpoint) {
1578
1642
  if (inGroup.length > 0)
1579
1643
  section.appendChild(renderParamGroup(title, inGroup));
1580
1644
  });
1581
- container.appendChild(section);
1645
+ left.appendChild(section);
1582
1646
  }
1583
1647
  const reqBody = data.requestBody;
1584
1648
  if (reqBody?.content) {
@@ -1586,12 +1650,10 @@ function renderEndpointDetails(endpoint) {
1586
1650
  section.className = 'endpoint-subsection';
1587
1651
  const contentType = Object.keys(reqBody.content)[0];
1588
1652
  section.innerHTML = `<div class="subsection-label">REQUEST BODY${reqBody.required ? ' <span style="color:#ef4444">required</span>' : ''}${contentType ? ` · ${esc(contentType)}` : ''}</div>`;
1589
- const content = reqBody.content[contentType];
1590
- if (content?.schema) {
1591
- renderSchemaTree(section, content.schema);
1592
- appendJsonExample(section, generateExampleFromSchema(content.schema));
1593
- }
1594
- container.appendChild(section);
1653
+ const bodyContent = reqBody.content[contentType];
1654
+ if (bodyContent?.schema)
1655
+ renderSchemaTree(section, bodyContent.schema);
1656
+ left.appendChild(section);
1595
1657
  }
1596
1658
  const responses = data.responses;
1597
1659
  if (responses && Object.keys(responses).length > 0) {
@@ -1611,14 +1673,25 @@ function renderEndpointDetails(endpoint) {
1611
1673
  if (first)
1612
1674
  first.classList.add('open');
1613
1675
  }
1614
- container.appendChild(section);
1676
+ left.appendChild(section);
1677
+ }
1678
+ // Right column: request samples (cURL / JavaScript / Python / Rust).
1679
+ const samplesSection = doc.createElement('div');
1680
+ samplesSection.className = 'endpoint-right-section';
1681
+ samplesSection.innerHTML = '<div class="endpoint-right-header">Request samples</div>';
1682
+ samplesSection.appendChild(renderCodeExamples(endpoint, data));
1683
+ right.appendChild(samplesSection);
1684
+ // Right column: response samples (JSON examples per status code).
1685
+ if (responses && Object.keys(responses).length > 0) {
1686
+ const respSamples = doc.createElement('div');
1687
+ respSamples.className = 'endpoint-right-section';
1688
+ respSamples.innerHTML = '<div class="endpoint-right-header">Response samples</div>';
1689
+ respSamples.appendChild(renderResponseSamples(responses));
1690
+ right.appendChild(respSamples);
1615
1691
  }
1616
- // Request samples (cURL / JavaScript / Python / Rust)
1617
- const examplesSection = doc.createElement('div');
1618
- examplesSection.className = 'endpoint-subsection';
1619
- examplesSection.innerHTML = '<div class="subsection-label">REQUEST SAMPLES</div>';
1620
- examplesSection.appendChild(renderCodeExamples(endpoint, data));
1621
- container.appendChild(examplesSection);
1692
+ content.appendChild(left);
1693
+ content.appendChild(right);
1694
+ container.appendChild(content);
1622
1695
  }
1623
1696
  if (activeProtocol === 'websocket') {
1624
1697
  appendInfoGrid(container, [['Channel Type', data.type], ['Path', endpoint.path]]);
@@ -1901,23 +1974,35 @@ function pageNavCard(entry, label, direction) {
1901
1974
  button.onclick = () => setDocsPage(entry.path);
1902
1975
  return button;
1903
1976
  }
1977
+ function setTocColumn(visible) {
1978
+ const shell = doc.querySelector?.('.main-shell');
1979
+ if (shell)
1980
+ shell.classList.toggle('main-shell-no-toc', !visible);
1981
+ }
1904
1982
  function renderToc(root) {
1905
1983
  const toc = byId('pageToc');
1906
1984
  if (!toc)
1907
1985
  return;
1908
1986
  toc.textContent = '';
1909
- if (tocConfig.enabled === false)
1987
+ if (tocConfig.enabled === false) {
1988
+ setTocColumn(false);
1910
1989
  return;
1911
- if (root.querySelector?.('[data-markdown-ignore-all="true"]'))
1990
+ }
1991
+ if (root.querySelector?.('[data-markdown-ignore-all="true"]')) {
1992
+ setTocColumn(false);
1912
1993
  return;
1994
+ }
1913
1995
  const min = Number(tocConfig.minLevel ?? 2);
1914
1996
  const max = Number(tocConfig.maxLevel ?? 3);
1915
1997
  const headings = Array.from(root.querySelectorAll('h1,h2,h3,h4,h5,h6') ?? []).filter((heading) => {
1916
1998
  const level = Number(heading.tagName.slice(1));
1917
1999
  return heading.id && level >= min && level <= max && heading.dataset?.markdownIgnore !== 'true';
1918
2000
  });
1919
- if (headings.length === 0)
2001
+ if (headings.length === 0) {
2002
+ setTocColumn(false);
1920
2003
  return;
2004
+ }
2005
+ setTocColumn(true);
1921
2006
  const title = doc.createElement('div');
1922
2007
  title.className = 'toc-title';
1923
2008
  title.textContent = 'On this page';