import * as plugins from './plugins.js'; import { docsIcons, type TDocsIconName } from './docs-icon.js'; import type { IDocsWebsiteManifest, IDocsWebsitePage, IDocsWebsiteSection, IDocsWebsiteVersion, } from './interfaces.js'; const themeStorageKey = 'tsdoc-theme'; interface ISearchResult { page: IDocsWebsitePage; headingId?: string; title: string; crumb: string; excerpt?: string; } export class TsdocApp extends plugins.DeesElement { public static styles = [ plugins.cssManager.defaultStyles, plugins.css` :host { --accent: #0071e3; --canvas: #0b0d10; --surface: #12151a; --surface-raised: #181c22; --surface-soft: #20252d; --text: #f5f7fa; --text-secondary: #a9b0bb; --text-tertiary: #747d8b; --line: rgba(255, 255, 255, 0.1); --header-bg: rgba(11, 13, 16, 0.82); --code-bg: #090b0e; --sidebar-width: 268px; display: block; min-height: 100vh; color: var(--text); background: var(--canvas); font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: 15px; line-height: 1.55; -webkit-font-smoothing: antialiased; } :host([gobright]) { --canvas: #ffffff; --surface: #ffffff; --surface-raised: #ffffff; --surface-soft: #f5f5f7; --text: #1d1d1f; --text-secondary: #515154; --text-tertiary: #86868b; --line: rgba(0, 0, 0, 0.1); --header-bg: rgba(255, 255, 255, 0.84); --code-bg: #f5f5f7; } * { box-sizing: border-box; } button, input { font: inherit; } button, a { -webkit-tap-highlight-color: transparent; } a { color: inherit; } .app { min-height: 100vh; background: var(--canvas); } .header { position: fixed; z-index: 40; inset: 0 0 auto 0; height: 61px; border-bottom: 1px solid var(--line); background: var(--header-bg); backdrop-filter: saturate(180%) blur(22px); } .header__inner { height: 100%; max-width: 1500px; margin: 0 auto; padding: 0 28px; display: flex; align-items: center; gap: 24px; } .brand { min-width: 240px; display: flex; align-items: center; gap: 10px; text-decoration: none; font-weight: 650; letter-spacing: -0.018em; } .brand__mark { width: 28px; height: 28px; display: grid; place-items: center; color: #fff; background: var(--accent); border-radius: 8px; font-size: 13px; font-weight: 750; box-shadow: 0 4px 16px color-mix(in srgb, var(--accent) 32%, transparent); } .brand__badge, .source-badge { padding: 2px 7px; border-radius: 999px; color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; } .header__spacer { flex: 1; } .search-button { width: min(360px, 32vw); height: 34px; padding: 0 8px 0 12px; display: flex; align-items: center; gap: 9px; color: var(--text-tertiary); border: 1px solid var(--line); border-radius: 9px; background: var(--surface-soft); cursor: pointer; text-align: left; transition: border-color 160ms ease, background 160ms ease; } .search-button:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); } .search-button span { flex: 1; } kbd { min-width: 24px; height: 21px; padding: 0 5px; display: inline-grid; place-items: center; border: 1px solid var(--line); border-radius: 5px; color: var(--text-tertiary); background: var(--surface-raised); font-size: 11px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06); } .icon-button, .menu-button { width: 34px; height: 34px; padding: 0; display: grid; place-items: center; color: var(--text-secondary); border: 1px solid transparent; border-radius: 8px; background: transparent; cursor: pointer; } .icon-button:hover, .menu-button:hover { color: var(--text); background: var(--surface-soft); } .menu-button { display: none; } tsdoc-icon { display: contents; } svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } .layout { max-width: 1500px; min-height: 100vh; margin: 0 auto; padding-top: 61px; display: grid; grid-template-columns: var(--sidebar-width) minmax(0, 1fr); } .sidebar { position: fixed; z-index: 30; top: 61px; bottom: 0; width: var(--sidebar-width); padding: 32px 22px 36px 28px; overflow-y: auto; border-right: 1px solid var(--line); background: var(--canvas); } .version { height: 34px; margin-bottom: 26px; padding: 0 10px; display: flex; align-items: center; justify-content: space-between; color: var(--text-secondary); border: 1px solid var(--line); border-radius: 8px; background: var(--surface); font-size: 12px; } .version strong { color: var(--text); font-weight: 600; } .nav-section { margin: 0 0 24px; } .nav-section__title { margin: 0 10px 7px; color: var(--text-tertiary); font-size: 11px; font-weight: 700; letter-spacing: 0.065em; text-transform: uppercase; } .nav-link { width: 100%; min-height: 34px; padding: 7px 10px; display: flex; align-items: center; gap: 8px; color: var(--text-secondary); border: 0; border-radius: 7px; background: transparent; cursor: pointer; text-align: left; font-size: 13px; line-height: 1.25; } .nav-link:hover { color: var(--text); background: var(--surface-soft); } .nav-link--active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); font-weight: 600; } .main { grid-column: 2; min-width: 0; display: grid; grid-template-columns: minmax(0, 790px) 220px; justify-content: center; gap: 72px; padding: 62px 52px 90px; } .article { min-width: 0; } .article__eyebrow { display: flex; align-items: center; gap: 8px; color: var(--accent); font-size: 12px; font-weight: 650; } .article h1 { margin: 11px 0 13px; color: var(--text); font-size: clamp(34px, 5vw, 48px); line-height: 1.08; letter-spacing: -0.045em; font-weight: 700; } .article__description { max-width: 680px; margin: 0 0 42px; color: var(--text-secondary); font-size: 19px; line-height: 1.5; letter-spacing: -0.012em; } .docs-content { color: var(--text-secondary); } .docs-content h2 { margin: 54px 0 17px; padding-top: 4px; color: var(--text); font-size: 26px; line-height: 1.22; letter-spacing: -0.025em; } .docs-content h3 { margin: 34px 0 12px; color: var(--text); font-size: 18px; line-height: 1.3; letter-spacing: -0.012em; } .docs-content p { margin: 0 0 18px; } .docs-content a { color: var(--accent); text-decoration: none; } .docs-content a:hover { text-decoration: underline; } .docs-content strong { color: var(--text); font-weight: 650; } .docs-content ul, .docs-content ol { margin: 0 0 22px; padding-left: 24px; } .docs-content li { margin: 8px 0; padding-left: 3px; } .docs-content img { max-width: 100%; height: auto; margin: 22px 0; border-radius: 12px; border: 1px solid var(--line); } .docs-content blockquote { margin: 24px 0; padding: 15px 18px; color: var(--text-secondary); border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0; background: color-mix(in srgb, var(--accent) 7%, transparent); } .docs-content :not(pre) > code, .docs-type, .docs-pill { font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; } .docs-content :not(pre) > code { padding: 2px 5px; color: var(--text); border: 1px solid var(--line); border-radius: 5px; background: var(--surface-soft); font-size: 0.88em; } .docs-code { margin: 24px 0 28px; overflow: hidden; border: 1px solid var(--line); border-radius: 11px; background: var(--code-bg); } .docs-code__header { height: 38px; padding: 0 12px 0 15px; display: flex; align-items: center; justify-content: space-between; color: var(--text-tertiary); border-bottom: 1px solid var(--line); font: 11px/1 "SFMono-Regular", Consolas, monospace; text-transform: uppercase; } .docs-code__header button { padding: 4px 7px; color: var(--text-secondary); border: 0; border-radius: 5px; background: transparent; cursor: pointer; font-size: 11px; } .docs-code__header button:hover { color: var(--text); background: var(--surface-soft); } .docs-code pre { margin: 0; padding: 18px 20px 20px; overflow-x: auto; } .docs-code code { color: var(--text); font: 13px/1.65 "SFMono-Regular", Consolas, "Liberation Mono", monospace; } .docs-api-kind { display: inline-flex; margin: 0 0 18px; padding: 4px 9px; color: var(--accent); border-radius: 999px; background: color-mix(in srgb, var(--accent) 10%, transparent); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; } .docs-signature { margin: 16px 0 24px; padding: 15px 17px; overflow-x: auto; color: var(--text); border: 1px solid var(--line); border-radius: 9px; background: var(--code-bg); } .docs-signature code { font: 13px/1.65 "SFMono-Regular", Consolas, monospace; } .docs-api-member, .docs-endpoint { scroll-margin-top: 84px; } .docs-parameters { margin: 16px 0 26px; border-top: 1px solid var(--line); } .docs-parameter { padding: 15px 0; border-bottom: 1px solid var(--line); } .docs-parameter > div:first-child { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; color: var(--text); } .docs-parameter p { margin: 7px 0 0; font-size: 13px; } .docs-type { color: var(--text-tertiary); font-size: 12px; } .docs-pill { padding: 2px 6px; color: var(--text-tertiary); border-radius: 999px; background: var(--surface-soft); font-size: 9px; text-transform: uppercase; } .docs-pill--required { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); } .docs-method { display: inline-flex; width: 58px; justify-content: center; padding: 5px 7px; border-radius: 6px; color: #fff; background: #6e7781; font-size: 10px; letter-spacing: 0.04em; vertical-align: 3px; } .docs-method--get { background: #16815a; } .docs-method--post { background: #0071e3; } .docs-method--put, .docs-method--patch { background: #aa6500; } .docs-method--delete { background: #c43737; } .docs-table-wrap { margin: 20px 0 30px; overflow-x: auto; border: 1px solid var(--line); border-radius: 9px; } .docs-content table { width: 100%; border-collapse: collapse; font-size: 13px; } .docs-content th, .docs-content td { padding: 11px 13px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; } .docs-content th { color: var(--text); background: var(--surface-soft); font-weight: 600; } .docs-content tbody tr:last-child td { border-bottom: 0; } .pager { margin-top: 70px; padding-top: 22px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; border-top: 1px solid var(--line); } .pager__link { min-height: 70px; padding: 13px 15px; display: flex; flex-direction: column; justify-content: center; color: var(--text-secondary); border: 1px solid var(--line); border-radius: 9px; background: var(--surface); cursor: pointer; } .pager__link:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); } .pager__link--next { text-align: right; } .pager__label { color: var(--text-tertiary); font-size: 11px; } .pager__title { color: var(--accent); font-size: 13px; font-weight: 600; } .outline { position: sticky; top: 96px; align-self: start; max-height: calc(100vh - 120px); overflow-y: auto; } .outline__title { margin-bottom: 10px; color: var(--text); font-size: 12px; font-weight: 650; } .outline__link { width: 100%; padding: 5px 0; display: block; color: var(--text-tertiary); border: 0; background: none; cursor: pointer; text-align: left; font-size: 12px; line-height: 1.35; } .outline__link--nested { padding-left: 12px; } .outline__link:hover { color: var(--accent); } .empty, .loading, .error { max-width: 560px; margin: 120px auto; padding: 32px; text-align: center; color: var(--text-secondary); } .empty h1, .error h1 { color: var(--text); letter-spacing: -0.03em; } .error code { display: block; margin-top: 12px; color: #d33; } .scrim { display: none; } .search-modal { position: fixed; z-index: 80; inset: 0; padding: 12vh 20px 20px; display: flex; justify-content: center; align-items: flex-start; background: rgba(0, 0, 0, 0.42); backdrop-filter: blur(7px); } .search-panel { width: min(650px, 100%); overflow: hidden; border: 1px solid var(--line); border-radius: 14px; background: var(--surface-raised); box-shadow: 0 28px 80px rgba(0, 0, 0, 0.28); } .search-input-wrap { height: 58px; padding: 0 17px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--line); } .search-input { flex: 1; min-width: 0; color: var(--text); border: 0; outline: 0; background: transparent; font-size: 17px; } .search-results { max-height: min(480px, 62vh); padding: 8px; overflow-y: auto; } .search-result { width: 100%; padding: 11px 12px; display: grid; grid-template-columns: 1fr auto; gap: 5px 14px; color: var(--text-secondary); border: 0; border-radius: 8px; background: transparent; cursor: pointer; text-align: left; } .search-result:hover { background: var(--surface-soft); } .search-result__title { color: var(--text); font-size: 14px; font-weight: 600; } .search-result__section { color: var(--text-tertiary); font-size: 11px; } .search-result__excerpt { grid-column: 1 / -1; overflow: hidden; color: var(--text-tertiary); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } .search-empty { padding: 32px 16px; text-align: center; color: var(--text-tertiary); } @media (max-width: 1199px) { .main { grid-template-columns: minmax(0, 790px); } .outline { display: none; } } @media (max-width: 959px) { .header__inner { padding: 0 16px; gap: 10px; } .brand { min-width: 0; flex: 1; } .brand__badge { display: none; } .header__spacer { display: none; } .menu-button { display: grid; } .search-button { width: 34px; padding: 0; justify-content: center; border-color: transparent; background: transparent; } .search-button span, .search-button kbd { display: none; } .layout { display: block; } .sidebar { width: min(310px, 86vw); transform: translateX(-105%); visibility: hidden; transition: transform 220ms cubic-bezier(.2, .8, .2, 1), visibility 0s linear 220ms; box-shadow: 18px 0 45px rgba(0, 0, 0, 0.18); } .sidebar--open { transform: translateX(0); visibility: visible; transition: transform 220ms cubic-bezier(.2, .8, .2, 1); } .scrim { position: fixed; z-index: 25; inset: 61px 0 0; display: block; background: rgba(0, 0, 0, 0.28); } .main { padding: 46px 24px 72px; } } @media (max-width: 560px) { .brand__name { max-width: 45vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .header__inner { padding: 0 12px; } .main { padding: 34px 19px 60px; } .article h1 { font-size: 35px; } .article__description { margin-bottom: 32px; font-size: 17px; } .docs-content h2 { margin-top: 44px; font-size: 23px; } .pager { grid-template-columns: 1fr; } .pager__link--next { text-align: left; } .search-modal { padding: 8px; align-items: flex-start; } .search-panel { margin-top: 50px; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition-duration: 0.01ms !important; } } `, plugins.css` :host { --accent: #5b53e6; --canvas: #000; --surface: #000; --surface-raised: #1d1d1f; --surface-soft: #2c2c2e; --text: #f5f5f7; --text-secondary: #d6d6db; --text-tertiary: #86868b; --line: #38383a; --line-strong: #48484a; --header-bg: rgba(22, 22, 23, 0.72); --code-bg: #1d1d1f; --code-header-bg: #161617; --note: #6cb4ff; --note-bg: rgba(41, 151, 255, 0.15); --warn: #ffb765; --warn-bg: rgba(255, 159, 10, 0.15); --sidebar-width: 268px; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.72; letter-spacing: -0.01em; text-rendering: optimizeLegibility; } :host([gobright]) { --canvas: #fff; --surface: #fff; --surface-raised: #fbfbfd; --surface-soft: #f5f5f7; --text: #1d1d1f; --text-secondary: #333336; --text-tertiary: #86868b; --line: #d2d2d7; --line-strong: #c0c0c6; --header-bg: rgba(251, 251, 253, 0.72); --code-bg: #f5f5f7; --code-header-bg: #ececef; --note: #0068c9; --note-bg: #eaf3fd; --warn: #bf5000; --warn-bg: #fdf1e7; } @keyframes tsdoc-fade { from { opacity: 0; } to { opacity: 1; } } @keyframes tsdoc-pop { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: none; } } @keyframes tsdoc-rise { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } } .header { position: sticky; inset: auto; top: 0; z-index: 50; height: 61px; padding: 0 22px; display: flex; align-items: center; gap: 14px; border-bottom: 1px solid var(--line); background: var(--header-bg); backdrop-filter: saturate(180%) blur(20px); } .header__inner { width: 100%; max-width: none; margin: 0; padding: 0; display: flex; align-items: center; gap: 14px; } .brand { min-width: 0; margin-right: 2px; gap: 10px; color: var(--text); font-size: 16px; font-weight: 600; letter-spacing: -0.02em; } .brand__mark { width: 25px; height: 25px; display: block; color: var(--accent); border-radius: 0; background: none; box-shadow: none; } .brand__badge { padding: 2px 8px; color: var(--text-tertiary); border-radius: 6px; background: var(--surface-soft); font-size: 11px; font-weight: 500; letter-spacing: 0; text-transform: none; } .menu-button, .icon-button { width: 36px; height: 36px; color: var(--text-tertiary); border: 1px solid var(--line); border-radius: 9px; background: var(--canvas); } .menu-button:hover, .icon-button:hover { color: var(--text); border-color: var(--line-strong); background: var(--canvas); } .version-picker { position: relative; } .version-button { height: 30px; padding: 0 8px 0 11px; display: flex; align-items: center; gap: 6px; color: var(--text-tertiary); border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); cursor: pointer; } .version-button:hover { border-color: var(--line-strong); } .version-button span, .version-option span { color: var(--text); font-family: 'SF Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 12.5px; letter-spacing: 0; } .version-button svg { width: 13px; height: 13px; stroke-width: 2.3; } .version-scrim { position: fixed; z-index: 55; inset: 0; border: 0; background: transparent; } .version-menu { position: absolute; z-index: 60; top: calc(100% + 7px); left: 0; min-width: 186px; padding: 5px; border: 1px solid var(--line); border-radius: 11px; background: var(--canvas); box-shadow: 0 14px 40px rgba(0, 0, 0, 0.16); animation: tsdoc-pop 140ms ease; } .version-option { width: 100%; padding: 7px 10px; display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--text); border: 0; border-radius: 7px; background: none; cursor: pointer; text-align: left; } .version-option:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); } .version-option svg { width: 14px; height: 14px; color: var(--accent); stroke-width: 2.6; } .search-button { min-width: 240px; width: auto; height: 36px; padding: 0 9px 0 13px; gap: 9px; color: var(--text-tertiary); border: 1px solid transparent; border-radius: 10px; background: var(--surface-soft); } .search-button:hover { border-color: var(--line-strong); background: var(--canvas); } .search-button span { font-size: 13.5px; } .search-button svg { width: 15px; height: 15px; } kbd { min-width: auto; height: auto; padding: 1px 5px; color: var(--text-tertiary); border: 1px solid var(--line); background: transparent; font-family: 'SF Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 11px; font-weight: 400; line-height: 1.45; letter-spacing: 0; box-shadow: none; } .layout { max-width: 1440px; min-height: calc(100vh - 61px); margin: 0 auto; padding-top: 0; grid-template-columns: 268px minmax(0, 1fr) 240px; align-items: start; } .sidebar { position: sticky; z-index: 30; top: 61px; bottom: auto; width: auto; height: calc(100vh - 61px); padding: 0; border-right: 1px solid var(--line); background: var(--canvas); } .nav { padding: 22px 12px 48px 18px; } .nav-section { margin: 0 0 4px; } .nav-section__button { width: 100%; padding: 7px 8px; display: flex; align-items: center; gap: 9px; color: var(--text); border: 0; border-radius: 7px; background: none; cursor: pointer; } .nav-section__button:hover { background: var(--surface-soft); } .nav-section__icon { width: 16px; height: 16px; display: inline-flex; flex: none; align-items: center; justify-content: center; color: var(--accent); } .nav-section__icon svg { width: 16px; height: 16px; display: block; } .nav-section__title { margin: 0; flex: 1; color: var(--text); font-size: 12.5px; font-weight: 600; line-height: 1.35; letter-spacing: 0.01em; text-align: left; text-transform: none; } .nav-section__chevron { width: 14px; height: 14px; display: inline-flex; flex: none; align-items: center; justify-content: center; color: var(--text-tertiary); stroke-width: 2.2; transition: transform 150ms ease; } .nav-section__chevron svg { width: 14px; height: 14px; display: block; } .nav-section__chevron--collapsed { transform: rotate(-90deg); } .nav-section__pages { margin: 2px 0 10px 17px; padding-left: 11px; display: flex; flex-direction: column; gap: 1px; border-left: 1px solid var(--line); } .nav-link { min-height: 0; padding: 6px 11px; color: var(--text-tertiary); border-radius: 7px; font-size: 13px; font-weight: 450; line-height: 1.45; } .nav-link:hover { color: var(--text); background: color-mix(in srgb, var(--accent) 10%, transparent); } .nav-link--active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); font-weight: 600; } .main { grid-column: auto; min-width: 0; display: block; padding: 38px 54px 96px; } .article { max-width: 772px; animation: tsdoc-rise 280ms ease; } .breadcrumbs { margin-bottom: 12px; display: flex; align-items: center; gap: 8px; color: var(--text-tertiary); font-family: 'SF Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-size: 12px; line-height: 1.45; letter-spacing: 0; } .breadcrumbs__separator { opacity: 0.5; } .breadcrumbs__page { color: var(--text-tertiary); } .version-banner { margin-bottom: 26px; padding: 12px 16px; display: flex; align-items: center; gap: 11px; color: var(--text-secondary); border-left: 3px solid var(--warn); border-radius: 12px; background: var(--warn-bg); font-size: 13.5px; line-height: 1.5; } .version-banner svg { width: 16px; height: 16px; flex: none; color: var(--warn); } .version-banner span { flex: 1; } .version-banner strong { color: var(--text); font-weight: 600; } .version-banner button { padding: 0; color: var(--accent); border: 0; background: none; cursor: pointer; font-size: 12.5px; font-weight: 600; white-space: nowrap; } .article h1 { margin: 0 0 30px; font-size: 40px; font-weight: 600; line-height: 1.08; letter-spacing: -0.03em; } .article__description { max-width: none; margin: 0 0 18px; color: var(--text-secondary); font-size: 15px; line-height: 1.72; letter-spacing: -0.01em; } .docs-content { color: var(--text-secondary); font-size: 15px; line-height: 1.72; } .docs-content h2 { margin: 46px 0 15px; padding-top: 0; font-size: 22px; font-weight: 600; line-height: 1.3; letter-spacing: -0.02em; scroll-margin-top: 82px; } .docs-content h3 { margin: 30px 0 11px; font-size: 16.5px; font-weight: 600; line-height: 1.35; scroll-margin-top: 82px; } .docs-content p { margin-bottom: 18px; } .docs-content ul, .docs-content ol { margin-bottom: 20px; padding-left: 22px; } .docs-content li { margin: 7px 0; } .docs-content li::marker { color: var(--accent); } .docs-content blockquote { margin: 0 0 20px; padding: 13px 17px; color: var(--text-secondary); border-left: 3px solid var(--note); border-radius: 12px; background: var(--note-bg); font-size: 14px; line-height: 1.62; } .docs-content :not(pre) > code { padding: 0.12em 0.4em; border: 0; background: var(--surface-soft); font-size: 0.86em; } .docs-code { margin: 0 0 21px; border-color: var(--line); border-radius: 14px; } .docs-code__header { height: auto; padding: 8px 9px 8px 15px; background: var(--code-header-bg); } .docs-code__header button { height: 27px; padding: 0 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--canvas); font-size: 12px; } .docs-code pre { padding: 16px; } .docs-code code { font-size: 13px; line-height: 1.66; } .docs-table-wrap { margin: 0 0 22px; border-radius: 12px; } .docs-content table { font-size: 13.5px; line-height: 1.55; } .docs-content th, .docs-content td { padding: 11px 16px; } .docs-content th { font-size: 12px; letter-spacing: 0.02em; text-transform: uppercase; } .docs-parameters { margin-bottom: 22px; overflow: hidden; border: 1px solid var(--line); border-radius: 12px; } .pager { margin-top: 60px; padding-top: 30px; gap: 13px; } .pager__link { min-height: 0; padding: 15px 17px; gap: 5px; border-radius: 12px; background: transparent; } .pager__link:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); } .pager__label { font-size: 12px; } .pager__title { color: var(--text); font-size: 15px; } .outline { position: sticky; top: 61px; height: calc(100vh - 61px); max-height: none; padding: 30px 22px 40px 6px; } .outline__title { margin-bottom: 13px; color: var(--text-tertiary); font-size: 11px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; } .outline__links { display: flex; flex-direction: column; border-left: 1px solid var(--line); } .outline__link { width: calc(100% + 1px); margin-left: -1px; padding: 4px 0 4px 15px; color: var(--text-tertiary); border-left: 2px solid transparent; font-size: 12.5px; line-height: 1.45; } .outline__link--nested { padding-left: 28px; } .outline__link:hover, .outline__link--active { color: var(--accent); } .outline__link--active { border-left-color: var(--accent); font-weight: 500; } .search-modal { z-index: 100; padding: 13vh 16px 20px; background: rgba(10, 11, 15, 0.5); backdrop-filter: blur(3px); animation: tsdoc-fade 120ms ease; } .search-panel { width: 100%; max-width: 600px; max-height: 68vh; border-color: var(--line-strong); border-radius: 15px; background: var(--canvas); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.4); animation: tsdoc-pop 170ms cubic-bezier(0.2, 0.8, 0.2, 1); } .search-input-wrap { height: auto; padding: 15px 18px; gap: 11px; } .search-input-wrap svg { width: 19px; height: 19px; color: var(--text-tertiary); } .search-input { font-size: 16px; } .search-results { padding: 7px; } .search-results__label { padding: 8px 11px 5px; color: var(--text-tertiary); font-size: 11px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; } .search-result { padding: 10px 12px; display: flex; align-items: flex-start; gap: 11px; border-radius: 9px; } .search-result:hover, .search-result--active { background: color-mix(in srgb, var(--accent) 10%, transparent); } .search-result__icon { width: 20px; display: grid; flex: none; place-items: center; color: var(--text-tertiary); } .search-result__icon svg { width: 15px; height: 15px; } .search-result__content { flex: 1; min-width: 0; } .search-result__title { display: block; color: var(--text); font-size: 14px; font-weight: 500; } .search-result__crumb { display: block; margin-top: 1px; color: var(--text-tertiary); font-size: 12px; } .search-result__excerpt { display: block; margin-top: 3px; color: var(--text-secondary); font-size: 12.5px; line-height: 1.5; } .search-result__enter { color: var(--text-tertiary); } .search-result__enter svg { width: 15px; height: 15px; } .search-footer { padding: 9px 16px; display: flex; gap: 18px; color: var(--text-tertiary); border-top: 1px solid var(--line); background: var(--surface-raised); font-size: 11.5px; } .search-footer span { display: flex; align-items: center; gap: 5px; } .search-footer b { font-family: 'SF Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; font-weight: 400; } @media (max-width: 1199px) { .layout { grid-template-columns: 268px minmax(0, 1fr); } } @media (max-width: 959px) { .header__inner { padding: 0; gap: 10px; } .version-picker { display: none; } .repository-button { display: none; } .search-button { min-width: 36px; width: 36px; border-color: var(--line); background: var(--canvas); } .sidebar { width: 286px; padding: 0; } .sidebar-scrim { position: fixed; z-index: 40; inset: 61px 0 0; display: block; border: 0; background: rgba(10, 11, 15, 0.42); animation: tsdoc-fade 140ms ease; } .main { padding: 26px 20px 76px; } } @media (max-width: 560px) { .brand__badge { display: none; } .article h1 { font-size: 34px; } .article__description { font-size: 15px; } .docs-content h2 { font-size: 22px; } .version-banner { align-items: flex-start; flex-wrap: wrap; } .search-footer { gap: 12px; } } `, plugins.css` @media (max-width: 1199px) { .outline { display: none; } } @media (max-width: 959px) { .layout { display: block; } .sidebar { position: fixed; z-index: 45; top: 61px; left: 0; width: 286px; height: calc(100vh - 61px); visibility: hidden; transform: translateX(-100%); box-shadow: 8px 0 30px rgba(0, 0, 0, 0.14); transition: transform 240ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 240ms; } .sidebar--open { visibility: visible; transform: translateX(0); transition: transform 240ms cubic-bezier(0.4, 0, 0.2, 1); } } @media (min-width: 960px) { .menu-button { display: none; } } `, ]; private manifest?: IDocsWebsiteManifest; private activePageId = ''; private loading = true; private errorMessage = ''; private searchOpen = false; private searchQuery = ''; private sidebarOpen = false; private versionOpen = false; private activeSearchIndex = 0; private activeHeadingId = ''; private collapsedSectionIds = new Set(); public async connectedCallback(): Promise { await super.connectedCallback(); window.addEventListener('hashchange', this.handleHashChange); window.addEventListener('keydown', this.handleGlobalKeydown); window.addEventListener('scroll', this.handleScroll, { passive: true }); await this.applyStoredTheme(); await this.loadManifest(); } public async disconnectedCallback(): Promise { window.removeEventListener('hashchange', this.handleHashChange); window.removeEventListener('keydown', this.handleGlobalKeydown); window.removeEventListener('scroll', this.handleScroll); await super.disconnectedCallback(); } protected themeChanged(): void { this.requestUpdate(); } private async applyStoredTheme(): Promise { const domtools = await this.domtoolsPromise; const storedTheme = localStorage.getItem(themeStorageKey); if (storedTheme === 'bright') domtools.themeManager.goBright(); if (storedTheme === 'dark') domtools.themeManager.goDark(); } private async loadManifest(): Promise { try { const response = await fetch('/docs-manifest.json', { cache: 'no-store' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const manifest = await response.json() as IDocsWebsiteManifest; if (manifest.schemaVersion !== '1.0' || !Array.isArray(manifest.pages) || !Array.isArray(manifest.sections)) { throw new Error('The docs manifest has an unsupported format.'); } if (!manifest.versions?.length) { manifest.versions = [{ label: manifest.version, current: true, latest: true, legacy: false, }]; } this.manifest = manifest; document.title = `${manifest.title} Documentation`; this.loading = false; this.syncRoute(); } catch (error) { this.loading = false; this.errorMessage = error instanceof Error ? error.message : String(error); this.requestUpdate(); } } private readonly handleHashChange = (): void => { this.syncRoute(); }; private readonly handleGlobalKeydown = (eventArg: KeyboardEvent): void => { if ((eventArg.metaKey || eventArg.ctrlKey) && eventArg.key.toLowerCase() === 'k') { eventArg.preventDefault(); this.openSearch(); return; } if (this.searchOpen && (eventArg.key === 'ArrowDown' || eventArg.key === 'ArrowUp')) { eventArg.preventDefault(); const results = this.searchResults; if (!results.length) return; const offset = eventArg.key === 'ArrowDown' ? 1 : -1; this.activeSearchIndex = Math.max(0, Math.min(results.length - 1, this.activeSearchIndex + offset)); this.requestUpdate(); return; } if (this.searchOpen && eventArg.key === 'Enter') { eventArg.preventDefault(); const result = this.searchResults[this.activeSearchIndex]; if (result) this.selectSearchResult(result); return; } if (eventArg.key === 'Escape') { this.searchOpen = false; this.sidebarOpen = false; this.versionOpen = false; this.activeSearchIndex = 0; this.requestUpdate(); } }; private readonly handleScroll = (): void => { const page = this.activePage; if (!page?.headings.length) return; let activeId = page.headings[0].id; for (const heading of page.headings) { const element = this.renderRoot.querySelector('#' + CSS.escape(heading.id)); if (element && element.getBoundingClientRect().top <= 104) activeId = heading.id; } if (activeId !== this.activeHeadingId) { this.activeHeadingId = activeId; this.requestUpdate(); } }; private syncRoute(): void { if (!this.manifest) return; const routeParts = window.location.hash.replace(/^#\/?/, '').split('/').filter(Boolean); const requestedPageId = routeParts[1]; const page = this.manifest.pages.find(candidate => candidate.id === requestedPageId) ?? this.orderedPages[0]; if (!page) { this.activePageId = ''; this.requestUpdate(); return; } this.activePageId = page.id; this.activeHeadingId = routeParts[2] ?? page.headings[0]?.id ?? ''; this.versionOpen = false; this.sidebarOpen = false; this.requestUpdate(); void this.updateComplete.then(() => { const headingId = routeParts[2]; if (headingId) { this.renderRoot.querySelector(`#${CSS.escape(headingId)}`)?.scrollIntoView({ block: 'start' }); } else { window.scrollTo({ top: 0, behavior: 'instant' }); } }); if (!requestedPageId) window.history.replaceState(null, '', page.route); } private get orderedPages(): IDocsWebsitePage[] { if (!this.manifest) return []; const pageMap = new Map(this.manifest.pages.map(page => [page.id, page])); return this.manifest.sections.flatMap(section => section.pageIds.map(id => pageMap.get(id)).filter((page): page is IDocsWebsitePage => Boolean(page))); } private get activePage(): IDocsWebsitePage | undefined { return this.manifest?.pages.find(page => page.id === this.activePageId) ?? this.orderedPages[0]; } private navigate(routeArg: string): void { if (window.location.hash === routeArg) this.syncRoute(); else window.location.hash = routeArg; this.searchOpen = false; this.searchQuery = ''; this.sidebarOpen = false; this.versionOpen = false; this.activeSearchIndex = 0; this.requestUpdate(); } private openSearch(): void { this.searchOpen = true; this.searchQuery = ''; this.activeSearchIndex = 0; this.requestUpdate(); void this.updateComplete.then(() => this.renderRoot.querySelector('.search-input')?.focus()); } private async toggleTheme(): Promise { const domtools = await this.domtoolsPromise; domtools.themeManager.toggleDarkBright(); localStorage.setItem(themeStorageKey, domtools.themeManager.goBrightBoolean ? 'bright' : 'dark'); } private readonly handleArticleClick = async (eventArg: Event): Promise => { const target = eventArg.target as HTMLElement | null; const copyButton = target?.closest('[data-copy-code]'); if (!copyButton) return; const code = copyButton.closest('.docs-code')?.querySelector('code')?.textContent ?? ''; await navigator.clipboard.writeText(code); copyButton.textContent = 'Copied'; window.setTimeout(() => { copyButton.textContent = 'Copy'; }, 1300); }; private icon(nameArg: TDocsIconName): plugins.TemplateResult { return plugins.html``; } private sectionIcon(sectionArg: IDocsWebsiteSection): plugins.TemplateResult { const title = sectionArg.title.toLowerCase(); if (title.includes('overview')) return this.icon('overview'); if (title.includes('getting') || title.includes('start')) return this.icon('start'); if (title.includes('concept')) return this.icon('concepts'); if (title.includes('integration')) return this.icon('integrations'); if (title.includes('api') || title.includes('reference')) return this.icon('api'); if (title.includes('operation') || title.includes('deploy')) return this.icon('operations'); return this.icon('book'); } private renderSidebarSection(sectionArg: IDocsWebsiteSection): plugins.TemplateResult { const pages = sectionArg.pageIds .map(id => this.manifest?.pages.find(page => page.id === id)) .filter((page): page is IDocsWebsitePage => Boolean(page)); const isCollapsed = this.collapsedSectionIds.has(sectionArg.id); return plugins.html` `; } private renderPager(pageArg: IDocsWebsitePage): plugins.TemplateResult { const pages = this.orderedPages; const index = pages.findIndex(page => page.id === pageArg.id); const previous = index > 0 ? pages[index - 1] : undefined; const next = index >= 0 ? pages[index + 1] : undefined; return plugins.html` `; } private get versions(): IDocsWebsiteVersion[] { return this.manifest?.versions?.length ? this.manifest.versions : [{ label: this.manifest?.version ?? 'current', current: true, latest: true, legacy: false, }]; } private get currentVersion(): IDocsWebsiteVersion { return this.versions.find(version => version.current) ?? this.versions[0]; } private get latestVersion(): IDocsWebsiteVersion { return this.versions.find(version => version.latest) ?? this.currentVersion; } private selectVersion(versionArg: IDocsWebsiteVersion): void { this.versionOpen = false; this.requestUpdate(); if (!versionArg.current && versionArg.href) window.location.assign(versionArg.href); } private get searchResults(): ISearchResult[] { const query = this.searchQuery.trim().toLowerCase(); if (!query) { return this.orderedPages.slice(0, 6).map(page => ({ page, title: page.title, crumb: page.sectionTitle, })); } const results: Array = []; for (const page of this.orderedPages) { const titleMatches = page.title.toLowerCase().includes(query); if (titleMatches) { results.push({ page, title: page.title, crumb: page.sectionTitle, excerpt: page.plainText.slice(0, 120), score: 0, }); } for (const heading of page.headings) { if (heading.text.toLowerCase().includes(query)) { results.push({ page, headingId: heading.id, title: heading.text, crumb: page.sectionTitle + ' › ' + page.title, score: 1, }); } } const bodyIndex = page.plainText.toLowerCase().indexOf(query); if (bodyIndex >= 0 && !titleMatches) { const excerptStart = Math.max(0, bodyIndex - 32); const excerptEnd = Math.min(page.plainText.length, excerptStart + 128); results.push({ page, title: page.title, crumb: page.sectionTitle, excerpt: (excerptStart > 0 ? '…' : '') + page.plainText.slice(excerptStart, excerptEnd) + (excerptEnd < page.plainText.length ? '…' : ''), score: 2, }); } } return results.sort((left, right) => left.score - right.score).slice(0, 8); } private selectSearchResult(resultArg: ISearchResult): void { const route = resultArg.headingId ? resultArg.page.route + '/' + resultArg.headingId : resultArg.page.route; this.navigate(route); } private highlightSearchText(textArg: string): plugins.TemplateResult { const query = this.searchQuery.trim(); if (!query) return plugins.html`${textArg}`; const index = textArg.toLowerCase().indexOf(query.toLowerCase()); if (index < 0) return plugins.html`${textArg}`; return plugins.html`${textArg.slice(0, index)}${textArg.slice(index, index + query.length)}${textArg.slice(index + query.length)}`; } private renderSearch(): plugins.TemplateResult { const results = this.searchResults; const activeIndex = results.length ? Math.max(0, Math.min(results.length - 1, this.activeSearchIndex)) : 0; return plugins.html` `; } public render(): plugins.TemplateResult { const page = this.activePage; const accent = this.manifest?.accentColor ?? '#5b53e6'; if (this.loading) return plugins.html`
Loading documentation…
`; if (this.errorMessage) return plugins.html`

Documentation unavailable

The in-memory manifest could not be loaded.

${this.errorMessage}
`; if (!this.manifest || !page) return plugins.html`

No documentation yet

Add Markdown, TypeDoc JSON, or an OpenAPI file to this docs repository.

`; const currentVersion = this.currentVersion; const latestVersion = this.latestVersion; const showVersionBanner = currentVersion.label !== latestVersion.label; return plugins.html`
${this.manifest.title} ${this.manifest.badge}
${this.versionOpen ? plugins.html` ` : ''}
${this.manifest.repositoryUrl ? plugins.html` ${this.icon('repository')} ` : ''}
${this.sidebarOpen ? plugins.html` ` : ''}
${showVersionBanner ? plugins.html`
${this.icon('warning')} You're viewing docs for ${currentVersion.label}. The latest release is ${latestVersion.label}.
` : ''}

${page.title}

${page.description ? plugins.html`

${page.description}

` : ''}
${plugins.unsafeHTML(page.html)}
${this.renderPager(page)}
${this.searchOpen ? this.renderSearch() : ''}
`; } } if (!customElements.get('tsdoc-app')) { customElements.define('tsdoc-app', TsdocApp); }