diff --git a/src/index.ts b/src/index.ts index e4d75ee..b1022f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,8 @@ import { filterPaths, registerSchemaPath } from './utils' import type { OpenAPIV3 } from 'openapi-types' import type { ElysiaSwaggerConfig } from './types' +import { SwaggerUIRender } from './swagger-ui' +import { ScalarRender } from './scalar' /** * Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate Swagger page. @@ -14,6 +16,8 @@ import type { ElysiaSwaggerConfig } from './types' export const swagger = ( { + provider = 'scalar', + scalarVersion = '1.12.4', documentation = {}, version = '5.9.0', excludeStaticFile = true, @@ -23,6 +27,8 @@ export const swagger = theme = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`, autoDarkMode = true }: ElysiaSwaggerConfig = { + provider: 'scalar', + scalarVersion: '1.12.4', documentation: {}, version: '5.9.0', excludeStaticFile: true, @@ -65,58 +71,8 @@ export const swagger = } ) - return new Response( - ` - - - - - ${info.title} - - - ${ - autoDarkMode && typeof theme === 'string' - ? ` - ` - : '' - } - ${ - typeof theme === 'string' - ? `` - : ` -` - } - - -
- - - -`, + return new Response(provider === 'swagger-ui' ? SwaggerUIRender(info, version, theme, stringifiedSwaggerOptions, autoDarkMode) : ScalarRender(`${relativePath}/json`, scalarVersion), { headers: { 'content-type': 'text/html; charset=utf8' diff --git a/src/scalar-elysia-theme.ts b/src/scalar-elysia-theme.ts new file mode 100644 index 0000000..f7f5105 --- /dev/null +++ b/src/scalar-elysia-theme.ts @@ -0,0 +1,155 @@ +export default `:root { + --theme-font: "Inter", var(--system-fonts); +} +/* basic theme */ +.light-mode { + --theme-color-1: #2a2f45; + --theme-color-2: #757575; + --theme-color-3: #8e8e8e; + --theme-color-accent: #f06292; + + --theme-background-1: #fff; + --theme-background-2: #f6f6f6; + --theme-background-3: #e7e7e7; + --theme-background-accent: #f062921f; + + --theme-border-color: rgba(0, 0, 0, 0.1); +} +.dark-mode { + --theme-color-1: rgba(255, 255, 255, 0.9); + --theme-color-2: rgba(156, 163, 175, 1); + --theme-color-3: rgba(255, 255, 255, 0.44); + --theme-color-accent: #f06292; + + --theme-background-1: #111728; + --theme-background-2: #1e293b; + --theme-background-3: #334155; + --theme-background-accent: #f062921f; + + --theme-border-color: rgba(255, 255, 255, 0.1); +} +/* Document Sidebar */ +.light-mode .sidebar, +.dark-mode .sidebar { + --sidebar-background-1: var(--theme-background-1); + --sidebar-item-hover-color: currentColor; + --sidebar-item-hover-background: var(--theme-background-2); + --sidebar-item-active-background: var(--theme-background-accent); + --sidebar-border-color: transparent; + --sidebar-color-1: var(--theme-color-1); + --sidebar-color-2: var(--theme-color-2); + --sidebar-color-active: var(--theme-color-accent); + --sidebar-search-background: transparent; + --sidebar-search-border-color: var(--theme-border-color); + --sidebar-search--color: var(--theme-color-3); +} + +/* advanced */ +.light-mode { + --theme-button-1: rgb(49 53 56); + --theme-button-1-color: #fff; + --theme-button-1-hover: rgb(28 31 33); + + --theme-color-green: #069061; + --theme-color-red: #ef0006; + --theme-color-yellow: #edbe20; + --theme-color-blue: #0082d0; + --theme-color-orange: #fb892c; + --theme-color-purple: #5203d1; + + --theme-scrollbar-color: rgba(0, 0, 0, 0.18); + --theme-scrollbar-color-active: rgba(0, 0, 0, 0.36); +} +.dark-mode { + --theme-button-1: #f6f6f6; + --theme-button-1-color: #000; + --theme-button-1-hover: #e7e7e7; + + --theme-color-green: #a3ffa9; + --theme-color-red: #ffa3a3; + --theme-color-yellow: #fffca3; + --theme-color-blue: #a5d6ff; + --theme-color-orange: #e2ae83; + --theme-color-purple: #d2a8ff; + + --theme-scrollbar-color: rgba(255, 255, 255, 0.24); + --theme-scrollbar-color-active: rgba(255, 255, 255, 0.48); +} +.section-container:first-of-type:after, +.section-container:first-of-type:before { + --stripes: repeating-linear-gradient( + 100deg, + #fff 0%, + #fff 0%, + transparent 2%, + transparent 12%, + #fff 17% + ); + --stripesDark: repeating-linear-gradient( + 100deg, + #000 0%, + #000 0%, + transparent 10%, + transparent 12%, + #000 17% + ); + --rainbow: repeating-linear-gradient( + 100deg, + #60a5fa 10%, + #e879f9 16%, + #5eead4 22%, + #60a5fa 30% + ); + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 200px; + background-image: var(--stripesDark), var(--rainbow); + background-size: 200%, 100%; + background-attachment: fixed; + mix-blend-mode: difference; + filter: opacity(10%) saturate(200%); + -webkit-mask-image: radial-gradient( + ellipse at 100% 0%, + black 40%, + transparent 75% + ); + mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 75%); + z-index: 0; + pointer-events: none; +} +.section-container:first-of-type:after { + mix-blend-mode: unset; +} +.light-mode .section-container:first-of-type:after, +.light-mode .section-container:first-of-type:before { + background-image: var(--stripes), var(--rainbow); + filter: opacity(4%) saturate(200%); + mix-blend-mode: unset; +} +.scalar-api-client__send-request-button, +.show-api-client-button { + background: #3c82f6 !important; +} +.show-api-client-button:before { + display: none; +} +.section-container .section:first-of-type { + border-bottom: 1px solid + var(--theme-border-color, var(--default-theme-border-color)); +} +.section-container[data-v-fcff76dc]:not(:first-of-type) { + border-top: none; +} +.sidebar-search:hover { + transition: all 0.15s ease-in-out; + --sidebar-search-border-color: var(--theme-color-accent) !important; + color: var(--sidebar-color-1) !important; +} +.scalar-api-client__container .sidebar { + --sidebar-border-color: var(--theme-border-color); +} +` \ No newline at end of file diff --git a/src/scalar.ts b/src/scalar.ts new file mode 100644 index 0000000..e3f26de --- /dev/null +++ b/src/scalar.ts @@ -0,0 +1,26 @@ +import scalarElysiaTheme from './scalar-elysia-theme' + +export const ScalarRender = (specUrl: string, version: string, customCss?: string) => ` + + + API Reference + + + + + + + + + +` \ No newline at end of file diff --git a/src/swagger-ui.ts b/src/swagger-ui.ts new file mode 100644 index 0000000..201a211 --- /dev/null +++ b/src/swagger-ui.ts @@ -0,0 +1,56 @@ +import { SwaggerInfo } from './types' + +export const SwaggerUIRender = (info: SwaggerInfo, version: string, theme: string | { + light: string + dark: string +}, stringifiedSwaggerOptions: string, autoDarkMode?: boolean) => ` + + + + + ${info.title} + + + ${ + autoDarkMode && typeof theme === 'string' + ? ` + ` + : '' + } + ${ + typeof theme === 'string' + ? `` + : ` +` + } + + +
+ + + +` \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index ab38959..f126eda 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,13 @@ import type { OpenAPIV3 } from 'openapi-types' import type { SwaggerUIOptions } from 'swagger-ui' + +export type SwaggerInfo = { + title: string + description: string + version: string +} + export interface ElysiaSwaggerConfig { /** * Customize Swagger config, refers to Swagger 2.0 config @@ -12,6 +19,21 @@ export interface ElysiaSwaggerConfig { | 'x-express-openapi-additional-middleware' | 'x-express-openapi-validation-strict' > + /** + * Choose your provider, Scalar or Swagger UI + * + * @default 'scalar' + * @see https://github.com/scalar/scalar + * @see https://github.com/swagger-api/swagger-ui + */ + scalarVersion?: string + /** + * Version to use for Scalar cdn bundle + * + * @default '1.12.4' + * @see https://github.com/scalar/scalar + */ + provider?: 'scalar' | 'swagger-ui' /** * Version to use for swagger cdn bundle *