From 31adcac3749105897cfec064f7cb7a1fdd618d36 Mon Sep 17 00:00:00 2001 From: dsainvg001 Date: Fri, 21 Aug 2026 14:11:24 +0530 Subject: [PATCH] Add Catppuccin, Everforest, and Dracula themes (#1) --- docs/api-v1.en.md | 4 + docs/api-v1.ja-jp.md | 4 + docs/api-v1.ko-kr.md | 4 + docs/api-v1.zh-cn.md | 4 + public/themes/catppuccin.css | 643 ++++++++++++++++++ public/themes/catppuccin.hljs.css | 205 ++++++ public/themes/dracula.css | 643 ++++++++++++++++++ public/themes/dracula.hljs.css | 205 ++++++ public/themes/everforest.css | 643 ++++++++++++++++++ public/themes/everforest.hljs.css | 205 ++++++ shared/templates/template-hash.generated.ts | 2 +- .../source/hljs/variants/catppuccin.css | 182 +++++ .../themes/source/hljs/variants/dracula.css | 182 +++++ .../source/hljs/variants/everforest.css | 182 +++++ .../themes/source/theme/tokens/catppuccin.css | 40 ++ shared/themes/source/theme/tokens/dracula.css | 40 ++ .../themes/source/theme/tokens/everforest.css | 40 ++ shared/themes/theme-registry.spec.ts | 18 + shared/themes/theme-registry.ts | 55 +- 19 files changed, 3299 insertions(+), 2 deletions(-) create mode 100644 public/themes/catppuccin.css create mode 100644 public/themes/catppuccin.hljs.css create mode 100644 public/themes/dracula.css create mode 100644 public/themes/dracula.hljs.css create mode 100644 public/themes/everforest.css create mode 100644 public/themes/everforest.hljs.css create mode 100644 shared/themes/source/hljs/variants/catppuccin.css create mode 100644 shared/themes/source/hljs/variants/dracula.css create mode 100644 shared/themes/source/hljs/variants/everforest.css create mode 100644 shared/themes/source/theme/tokens/catppuccin.css create mode 100644 shared/themes/source/theme/tokens/dracula.css create mode 100644 shared/themes/source/theme/tokens/everforest.css diff --git a/docs/api-v1.en.md b/docs/api-v1.en.md index 7789fbd..de31891 100644 --- a/docs/api-v1.en.md +++ b/docs/api-v1.en.md @@ -46,7 +46,11 @@ Requests without a valid key return `401 Unauthorized`. - `default` - `resume` +- `github` - `matrix` +- `catppuccin` +- `everforest` +- `dracula` ### Slugs diff --git a/docs/api-v1.ja-jp.md b/docs/api-v1.ja-jp.md index b752622..8b3b3f9 100644 --- a/docs/api-v1.ja-jp.md +++ b/docs/api-v1.ja-jp.md @@ -46,7 +46,11 @@ curl https://mdto.page/api/v1/pages \ - `default` - `resume` +- `github` - `matrix` +- `catppuccin` +- `everforest` +- `dracula` ### スラッグ diff --git a/docs/api-v1.ko-kr.md b/docs/api-v1.ko-kr.md index 9db6d16..dafb22a 100644 --- a/docs/api-v1.ko-kr.md +++ b/docs/api-v1.ko-kr.md @@ -46,7 +46,11 @@ curl https://mdto.page/api/v1/pages \ - `default` - `resume` +- `github` - `matrix` +- `catppuccin` +- `everforest` +- `dracula` ### 슬러그 diff --git a/docs/api-v1.zh-cn.md b/docs/api-v1.zh-cn.md index 207d240..aa69791 100644 --- a/docs/api-v1.zh-cn.md +++ b/docs/api-v1.zh-cn.md @@ -46,7 +46,11 @@ curl https://mdto.page/api/v1/pages \ - `default` - `resume` +- `github` - `matrix` +- `catppuccin` +- `everforest` +- `dracula` ### slug diff --git a/public/themes/catppuccin.css b/public/themes/catppuccin.css new file mode 100644 index 0000000..dc5d53c --- /dev/null +++ b/public/themes/catppuccin.css @@ -0,0 +1,643 @@ +/* Generated theme bundle for "catppuccin". Do not edit directly. */ + +/* Source: theme/tokens/catppuccin.css */ +:root { + --bg-root: #eff1f5; + --bg-card: #e6e9ef; + --border-subtle: #bcc0cc; + --text-primary: #4c4f69; + --text-secondary: #1e66f5; + --text-tertiary: #6c6f85; + --primary-color: #8839ef; + --code-bg: #e6e9ef; + --code-border: #ccd0da; + --shadow-card: 0 1px 2px rgba(76, 79, 105, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #1e1e2e; + --bg-card: #181825; + --border-subtle: #45475a; + --text-primary: #cdd6f4; + --text-secondary: #89b4fa; + --text-tertiary: #a6adc8; + --primary-color: #cba6f7; + --code-bg: #181825; + --code-border: #313244; + --shadow-card: 0 2px 6px rgba(17, 17, 27, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #1e1e2e; + --bg-card: #181825; + --border-subtle: #45475a; + --text-primary: #cdd6f4; + --text-secondary: #89b4fa; + --text-tertiary: #a6adc8; + --primary-color: #cba6f7; + --code-bg: #181825; + --code-border: #313244; + --shadow-card: 0 2px 6px rgba(17, 17, 27, 0.35); +} + +/* Source: theme/base.css */ +.content { + flex: 1; +} + +.top-actions { + position: absolute; + top: 16px; + right: 16px; + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.export-container { + position: relative; +} + +.font-size-controls { + display: flex; + align-items: center; + gap: 4px; + margin: 0; + padding: 0; + border: 0; + min-inline-size: auto; +} + +.font-size-btn { + min-width: 32px; + justify-content: center; +} + +.font-size-icon { + width: 18px; + height: 18px; +} + +.font-size-btn[disabled] { + cursor: default; + opacity: 0.35; + pointer-events: none; +} + +.export-option span { + flex: 1; +} + +.export-option.copied .option-icon { + display: none; +} + +.export-option.copied .option-check { + display: block; +} + +.mermaid { + text-align: center; +} + +@media print { + .top-actions { + display: none; + } + + footer { + display: none; + } + + :root, + body { + background-color: var(--bg-root); + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } +} + +@media (max-width: 640px) { + .top-actions { + top: 12px; + right: 12px; + gap: 6px; + } +} + +/* Source: theme/presets/article.css */ +body { + font-family: + "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + line-height: 1.6; + max-width: 720px; + margin: 0 auto; + padding-top: 88px; + padding-bottom: 48px; + padding-right: 24px; + padding-left: 24px; + background-color: var(--bg-root); + color: var(--text-primary); + -webkit-font-smoothing: antialiased; + display: flex; + flex-direction: column; + min-height: 100vh; + box-sizing: border-box; + transition: + background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), + color 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} + +.frontmatter-container { + margin-bottom: 2rem; + padding-bottom: 1.5rem; +} + +.frontmatter-row { + display: flex; + align-items: baseline; + margin-bottom: 0.5rem; + font-size: 0.95em; +} + +.frontmatter-label { + width: 140px; + color: var(--text-tertiary); + flex-shrink: 0; +} + +.frontmatter-value { + color: var(--text-primary); + flex: 1; +} + +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: var(--text-primary); + margin-top: 2.5em; + margin-bottom: 0.75em; + line-height: 1.2; + letter-spacing: -0.02em; + font-weight: 600; +} + +.content h1:first-child { + margin-top: 0; +} + +.content h1 { + font-size: 2em; + letter-spacing: -0.03em; + border-bottom: 1px solid var(--border-subtle); + padding-bottom: 0.5em; +} + +.content h2 { + font-size: 1.5em; +} + +.content h3, +.content h4, +.content h5, +.content h6 { + font-size: 1.25em; +} + +.content p { + margin-bottom: 1.5em; + color: var(--text-primary); + opacity: 0.95; +} + +.content code { + background-color: var(--code-bg); + padding: 2px 5px; + border-radius: 4px; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; + font-size: 0.9em; + color: var(--primary-color); + border: 1px solid var(--code-border); +} + +.content ul, +.content ol { + margin-bottom: 1.5em; + padding-left: 1.5em; + color: var(--text-tertiary); +} + +.content ul ul, +.content ul ol, +.content ol ul, +.content ol ol { + margin-top: 0.5em; + margin-bottom: 0; +} + +.content li { + margin-bottom: 0.5em; + color: var(--text-primary); +} + +.content li p { + margin: 0; +} + +.content a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: border-color 0.2s; +} + +.content a:hover { + border-color: var(--primary-color); +} + +.content blockquote { + border-left: 3px solid var(--primary-color); + margin: 0 0 1.5em 0; + padding-left: 1.2em; + color: var(--text-tertiary); + font-style: italic; +} + +.content table { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border-collapse: collapse; + margin-bottom: 1.5em; + font-size: 0.95em; +} + +.content th, +.content td { + padding: 10px; + border-bottom: 1px solid var(--border-subtle); + text-align: left; + white-space: nowrap; +} + +.content th { + color: var(--text-primary); + font-weight: 600; +} + +.content img { + max-width: 100%; + border-radius: 8px; + margin-bottom: 1.5em; +} + +.content hr { + border: none; + border-top: 1px solid var(--border-subtle); + margin: 3em 0; +} + +.content li:has(input[type="checkbox"]), +.content li.task-list-item { + list-style-type: none; +} + +.content ul:has(li input[type="checkbox"]) { + padding-left: 0; +} + +.content input[type="checkbox"] { + margin-left: 0; +} + +footer { + margin-top: 4rem; + padding-top: 2rem; + text-align: center; + font-size: 0.875rem; + color: var(--text-tertiary); +} + +footer p { + margin: 0; +} + +footer a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s; +} + +footer a:hover { + color: var(--text-primary); +} + +.top-actions button { + display: flex; + align-items: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.top-actions button:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.top-actions button svg { + width: 16px; + height: 16px; +} + +.export-btn { + display: flex; + align-items: center; + padding: 8px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.export-btn:hover { + background-color: var(--bg-card); + border-color: var(--border-subtle); + opacity: 1; +} + +.export-btn svg { + width: 18px; + height: 18px; +} + +.export-dropdown { + position: absolute; + top: calc(100% + 4px); + right: 0; + background-color: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: 8px; + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.06), + 0 12px 24px rgba(0, 0, 0, 0.08); + min-width: 180px; + padding: 6px; + opacity: 0; + visibility: hidden; + transform: translateY(-8px) scale(0.96); + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 100; + transform-origin: top right; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.export-dropdown.show { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); +} + +.export-option { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 7px 10px; + background: transparent; + border: none; + border-radius: 6px; + text-align: left; + cursor: pointer; + font-size: 13px; + font-weight: 500; + color: var(--text-primary); + transition: all 0.1s ease; + position: relative; +} + +.export-option:hover { + background-color: rgba(94, 106, 210, 0.1); + color: var(--primary-color); +} + +.export-option .option-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + opacity: 0.6; + stroke-width: 2; + transition: opacity 0.1s ease; +} + +.export-option:hover .option-icon { + opacity: 1; +} + +.export-option .option-check { + width: 16px; + height: 16px; + flex-shrink: 0; + display: none; + stroke-width: 2.5; +} + +/* Source: theme/features/default.css */ +.toc-toggle { + position: fixed; + top: 16px; + left: 16px; + z-index: 140; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.toc-toggle:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.toc-toggle svg { + width: 16px; + height: 16px; + color: inherit; +} + +.toc-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(2px); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 110; +} + +.toc-panel { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 280px; + padding: 24px; + padding-top: 68px; + background-color: var(--bg-root); + border-right: 1px solid var(--border-subtle); + transform: translateX(-100%); + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); + overflow-y: auto; + z-index: 130; + opacity: 0; +} + +.toc-header { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-tertiary); + margin-bottom: 16px; + padding-left: 8px; +} + +.toc-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; +} + +.toc-item { + margin: 0; + position: relative; +} + +.toc-link { + display: block; + padding: 6px 8px; + color: var(--text-tertiary); + text-decoration: none; + font-size: 13px; + line-height: 1.5; + transition: all 0.15s ease; + border-radius: 6px; + position: relative; +} + +.toc-link:hover { + color: var(--text-primary); + background-color: var(--bg-card); +} + +.toc-item[data-level="2"] .toc-link { + padding-left: 8px; +} + +.toc-item[data-level="3"] .toc-link { + padding-left: 20px; +} + +.toc-item[data-level="4"] .toc-link { + padding-left: 32px; +} + +.toc-item[data-level="5"] .toc-link { + padding-left: 44px; +} + +.toc-item[data-level="6"] .toc-link { + padding-left: 56px; +} + +body.toc-open .toc-backdrop { + opacity: 1; + visibility: visible; +} + +body.toc-open .toc-panel { + transform: translateX(0); + opacity: 1; +} + +.theme-toggle .icon-sun { + display: none; +} + +.theme-toggle .icon-moon { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-sun { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-moon { + display: none; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .theme-toggle .icon-sun { + display: block; + } + + :root:not([data-theme="light"]) .theme-toggle .icon-moon { + display: none; + } +} + +:root[data-theme="dark"] .export-dropdown { + background-color: rgba(22, 23, 26, 0.9); + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.2), + 0 12px 24px rgba(0, 0, 0, 0.4); +} + +:root[data-theme="dark"] .export-option:hover { + background-color: rgba(127, 138, 239, 0.15); + color: var(--primary-color); +} + +@media print { + .toc-toggle, + .toc-panel, + .toc-backdrop { + display: none; + } +} diff --git a/public/themes/catppuccin.hljs.css b/public/themes/catppuccin.hljs.css new file mode 100644 index 0000000..a7eedc0 --- /dev/null +++ b/public/themes/catppuccin.hljs.css @@ -0,0 +1,205 @@ +/* Generated highlight theme bundle for "catppuccin". Do not edit directly. */ + +/* Source: hljs/base.css */ +pre { + background-color: var(--code-bg); + border: 1px solid var(--code-border); + overflow-x: auto; + margin-bottom: 1.5em; +} + +pre code, +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; + background-color: transparent; + color: inherit; + border: none; + font-size: 0.85em; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; +} + +/* Source: hljs/variants/catppuccin.css */ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #e6e9ef; + color: #4c4f69; +} + +.hljs-comment, +.hljs-quote { + color: #9ca0b0; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #8839ef; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #1e66f5; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #df8e1d; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #40a02b; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #fe640b; +} + +.hljs-deletion { + color: #d20f39; + display: inline-block; + width: 100%; + background-color: rgba(210, 15, 57, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #181825; + color: #cdd6f4; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #6c7086; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #cba6f7; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #89b4fa; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #f9e2af; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #a6e3a1; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #fab387; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #f38ba8; + background-color: rgba(243, 139, 168, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #181825; + color: #cdd6f4; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #6c7086; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #cba6f7; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #89b4fa; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #f9e2af; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #a6e3a1; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #fab387; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #f38ba8; + background-color: rgba(243, 139, 168, 0.15); +} diff --git a/public/themes/dracula.css b/public/themes/dracula.css new file mode 100644 index 0000000..3ac7439 --- /dev/null +++ b/public/themes/dracula.css @@ -0,0 +1,643 @@ +/* Generated theme bundle for "dracula". Do not edit directly. */ + +/* Source: theme/tokens/dracula.css */ +:root { + --bg-root: #f8f8f2; + --bg-card: #e9e9f4; + --border-subtle: #d6d6e5; + --text-primary: #282a36; + --text-secondary: #bd93f9; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #e9e9f4; + --code-border: #d6d6e5; + --shadow-card: 0 1px 2px rgba(40, 42, 54, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #282a36; + --bg-card: #21222c; + --border-subtle: #44475a; + --text-primary: #f8f8f2; + --text-secondary: #8be9fd; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #21222c; + --code-border: #44475a; + --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #282a36; + --bg-card: #21222c; + --border-subtle: #44475a; + --text-primary: #f8f8f2; + --text-secondary: #8be9fd; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #21222c; + --code-border: #44475a; + --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.35); +} + +/* Source: theme/base.css */ +.content { + flex: 1; +} + +.top-actions { + position: absolute; + top: 16px; + right: 16px; + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.export-container { + position: relative; +} + +.font-size-controls { + display: flex; + align-items: center; + gap: 4px; + margin: 0; + padding: 0; + border: 0; + min-inline-size: auto; +} + +.font-size-btn { + min-width: 32px; + justify-content: center; +} + +.font-size-icon { + width: 18px; + height: 18px; +} + +.font-size-btn[disabled] { + cursor: default; + opacity: 0.35; + pointer-events: none; +} + +.export-option span { + flex: 1; +} + +.export-option.copied .option-icon { + display: none; +} + +.export-option.copied .option-check { + display: block; +} + +.mermaid { + text-align: center; +} + +@media print { + .top-actions { + display: none; + } + + footer { + display: none; + } + + :root, + body { + background-color: var(--bg-root); + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } +} + +@media (max-width: 640px) { + .top-actions { + top: 12px; + right: 12px; + gap: 6px; + } +} + +/* Source: theme/presets/article.css */ +body { + font-family: + "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + line-height: 1.6; + max-width: 720px; + margin: 0 auto; + padding-top: 88px; + padding-bottom: 48px; + padding-right: 24px; + padding-left: 24px; + background-color: var(--bg-root); + color: var(--text-primary); + -webkit-font-smoothing: antialiased; + display: flex; + flex-direction: column; + min-height: 100vh; + box-sizing: border-box; + transition: + background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), + color 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} + +.frontmatter-container { + margin-bottom: 2rem; + padding-bottom: 1.5rem; +} + +.frontmatter-row { + display: flex; + align-items: baseline; + margin-bottom: 0.5rem; + font-size: 0.95em; +} + +.frontmatter-label { + width: 140px; + color: var(--text-tertiary); + flex-shrink: 0; +} + +.frontmatter-value { + color: var(--text-primary); + flex: 1; +} + +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: var(--text-primary); + margin-top: 2.5em; + margin-bottom: 0.75em; + line-height: 1.2; + letter-spacing: -0.02em; + font-weight: 600; +} + +.content h1:first-child { + margin-top: 0; +} + +.content h1 { + font-size: 2em; + letter-spacing: -0.03em; + border-bottom: 1px solid var(--border-subtle); + padding-bottom: 0.5em; +} + +.content h2 { + font-size: 1.5em; +} + +.content h3, +.content h4, +.content h5, +.content h6 { + font-size: 1.25em; +} + +.content p { + margin-bottom: 1.5em; + color: var(--text-primary); + opacity: 0.95; +} + +.content code { + background-color: var(--code-bg); + padding: 2px 5px; + border-radius: 4px; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; + font-size: 0.9em; + color: var(--primary-color); + border: 1px solid var(--code-border); +} + +.content ul, +.content ol { + margin-bottom: 1.5em; + padding-left: 1.5em; + color: var(--text-tertiary); +} + +.content ul ul, +.content ul ol, +.content ol ul, +.content ol ol { + margin-top: 0.5em; + margin-bottom: 0; +} + +.content li { + margin-bottom: 0.5em; + color: var(--text-primary); +} + +.content li p { + margin: 0; +} + +.content a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: border-color 0.2s; +} + +.content a:hover { + border-color: var(--primary-color); +} + +.content blockquote { + border-left: 3px solid var(--primary-color); + margin: 0 0 1.5em 0; + padding-left: 1.2em; + color: var(--text-tertiary); + font-style: italic; +} + +.content table { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border-collapse: collapse; + margin-bottom: 1.5em; + font-size: 0.95em; +} + +.content th, +.content td { + padding: 10px; + border-bottom: 1px solid var(--border-subtle); + text-align: left; + white-space: nowrap; +} + +.content th { + color: var(--text-primary); + font-weight: 600; +} + +.content img { + max-width: 100%; + border-radius: 8px; + margin-bottom: 1.5em; +} + +.content hr { + border: none; + border-top: 1px solid var(--border-subtle); + margin: 3em 0; +} + +.content li:has(input[type="checkbox"]), +.content li.task-list-item { + list-style-type: none; +} + +.content ul:has(li input[type="checkbox"]) { + padding-left: 0; +} + +.content input[type="checkbox"] { + margin-left: 0; +} + +footer { + margin-top: 4rem; + padding-top: 2rem; + text-align: center; + font-size: 0.875rem; + color: var(--text-tertiary); +} + +footer p { + margin: 0; +} + +footer a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s; +} + +footer a:hover { + color: var(--text-primary); +} + +.top-actions button { + display: flex; + align-items: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.top-actions button:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.top-actions button svg { + width: 16px; + height: 16px; +} + +.export-btn { + display: flex; + align-items: center; + padding: 8px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.export-btn:hover { + background-color: var(--bg-card); + border-color: var(--border-subtle); + opacity: 1; +} + +.export-btn svg { + width: 18px; + height: 18px; +} + +.export-dropdown { + position: absolute; + top: calc(100% + 4px); + right: 0; + background-color: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: 8px; + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.06), + 0 12px 24px rgba(0, 0, 0, 0.08); + min-width: 180px; + padding: 6px; + opacity: 0; + visibility: hidden; + transform: translateY(-8px) scale(0.96); + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 100; + transform-origin: top right; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.export-dropdown.show { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); +} + +.export-option { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 7px 10px; + background: transparent; + border: none; + border-radius: 6px; + text-align: left; + cursor: pointer; + font-size: 13px; + font-weight: 500; + color: var(--text-primary); + transition: all 0.1s ease; + position: relative; +} + +.export-option:hover { + background-color: rgba(94, 106, 210, 0.1); + color: var(--primary-color); +} + +.export-option .option-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + opacity: 0.6; + stroke-width: 2; + transition: opacity 0.1s ease; +} + +.export-option:hover .option-icon { + opacity: 1; +} + +.export-option .option-check { + width: 16px; + height: 16px; + flex-shrink: 0; + display: none; + stroke-width: 2.5; +} + +/* Source: theme/features/default.css */ +.toc-toggle { + position: fixed; + top: 16px; + left: 16px; + z-index: 140; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.toc-toggle:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.toc-toggle svg { + width: 16px; + height: 16px; + color: inherit; +} + +.toc-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(2px); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 110; +} + +.toc-panel { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 280px; + padding: 24px; + padding-top: 68px; + background-color: var(--bg-root); + border-right: 1px solid var(--border-subtle); + transform: translateX(-100%); + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); + overflow-y: auto; + z-index: 130; + opacity: 0; +} + +.toc-header { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-tertiary); + margin-bottom: 16px; + padding-left: 8px; +} + +.toc-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; +} + +.toc-item { + margin: 0; + position: relative; +} + +.toc-link { + display: block; + padding: 6px 8px; + color: var(--text-tertiary); + text-decoration: none; + font-size: 13px; + line-height: 1.5; + transition: all 0.15s ease; + border-radius: 6px; + position: relative; +} + +.toc-link:hover { + color: var(--text-primary); + background-color: var(--bg-card); +} + +.toc-item[data-level="2"] .toc-link { + padding-left: 8px; +} + +.toc-item[data-level="3"] .toc-link { + padding-left: 20px; +} + +.toc-item[data-level="4"] .toc-link { + padding-left: 32px; +} + +.toc-item[data-level="5"] .toc-link { + padding-left: 44px; +} + +.toc-item[data-level="6"] .toc-link { + padding-left: 56px; +} + +body.toc-open .toc-backdrop { + opacity: 1; + visibility: visible; +} + +body.toc-open .toc-panel { + transform: translateX(0); + opacity: 1; +} + +.theme-toggle .icon-sun { + display: none; +} + +.theme-toggle .icon-moon { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-sun { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-moon { + display: none; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .theme-toggle .icon-sun { + display: block; + } + + :root:not([data-theme="light"]) .theme-toggle .icon-moon { + display: none; + } +} + +:root[data-theme="dark"] .export-dropdown { + background-color: rgba(22, 23, 26, 0.9); + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.2), + 0 12px 24px rgba(0, 0, 0, 0.4); +} + +:root[data-theme="dark"] .export-option:hover { + background-color: rgba(127, 138, 239, 0.15); + color: var(--primary-color); +} + +@media print { + .toc-toggle, + .toc-panel, + .toc-backdrop { + display: none; + } +} diff --git a/public/themes/dracula.hljs.css b/public/themes/dracula.hljs.css new file mode 100644 index 0000000..d2abd5b --- /dev/null +++ b/public/themes/dracula.hljs.css @@ -0,0 +1,205 @@ +/* Generated highlight theme bundle for "dracula". Do not edit directly. */ + +/* Source: hljs/base.css */ +pre { + background-color: var(--code-bg); + border: 1px solid var(--code-border); + overflow-x: auto; + margin-bottom: 1.5em; +} + +pre code, +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; + background-color: transparent; + color: inherit; + border: none; + font-size: 0.85em; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; +} + +/* Source: hljs/variants/dracula.css */ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #e9e9f4; + color: #282a36; +} + +.hljs-comment, +.hljs-quote { + color: #6272a4; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #bd93f9; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #0073e6; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #b45309; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #15803d; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #c2410c; +} + +.hljs-deletion { + color: #ff5555; + display: inline-block; + width: 100%; + background-color: rgba(255, 85, 85, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #21222c; + color: #f8f8f2; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #6272a4; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #ff79c6; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #bd93f9; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #8be9fd; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #f1fa8c; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #ffb86c; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #ff5555; + background-color: rgba(255, 85, 85, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #21222c; + color: #f8f8f2; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #6272a4; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #ff79c6; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #bd93f9; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #8be9fd; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #f1fa8c; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #ffb86c; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #ff5555; + background-color: rgba(255, 85, 85, 0.15); +} diff --git a/public/themes/everforest.css b/public/themes/everforest.css new file mode 100644 index 0000000..9bfc008 --- /dev/null +++ b/public/themes/everforest.css @@ -0,0 +1,643 @@ +/* Generated theme bundle for "everforest". Do not edit directly. */ + +/* Source: theme/tokens/everforest.css */ +:root { + --bg-root: #fdf6e3; + --bg-card: #f4f0d9; + --border-subtle: #e0dcc7; + --text-primary: #5c6a72; + --text-secondary: #879a39; + --text-tertiary: #939f91; + --primary-color: #879a39; + --code-bg: #f4f0d9; + --code-border: #e0dcc7; + --shadow-card: 0 1px 2px rgba(92, 106, 114, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #2b3339; + --bg-card: #323c41; + --border-subtle: #414b50; + --text-primary: #d3c6aa; + --text-secondary: #a7c080; + --text-tertiary: #859289; + --primary-color: #a7c080; + --code-bg: #232a2e; + --code-border: #3a454a; + --shadow-card: 0 2px 6px rgba(26, 31, 35, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #2b3339; + --bg-card: #323c41; + --border-subtle: #414b50; + --text-primary: #d3c6aa; + --text-secondary: #a7c080; + --text-tertiary: #859289; + --primary-color: #a7c080; + --code-bg: #232a2e; + --code-border: #3a454a; + --shadow-card: 0 2px 6px rgba(26, 31, 35, 0.35); +} + +/* Source: theme/base.css */ +.content { + flex: 1; +} + +.top-actions { + position: absolute; + top: 16px; + right: 16px; + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.export-container { + position: relative; +} + +.font-size-controls { + display: flex; + align-items: center; + gap: 4px; + margin: 0; + padding: 0; + border: 0; + min-inline-size: auto; +} + +.font-size-btn { + min-width: 32px; + justify-content: center; +} + +.font-size-icon { + width: 18px; + height: 18px; +} + +.font-size-btn[disabled] { + cursor: default; + opacity: 0.35; + pointer-events: none; +} + +.export-option span { + flex: 1; +} + +.export-option.copied .option-icon { + display: none; +} + +.export-option.copied .option-check { + display: block; +} + +.mermaid { + text-align: center; +} + +@media print { + .top-actions { + display: none; + } + + footer { + display: none; + } + + :root, + body { + background-color: var(--bg-root); + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } +} + +@media (max-width: 640px) { + .top-actions { + top: 12px; + right: 12px; + gap: 6px; + } +} + +/* Source: theme/presets/article.css */ +body { + font-family: + "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + line-height: 1.6; + max-width: 720px; + margin: 0 auto; + padding-top: 88px; + padding-bottom: 48px; + padding-right: 24px; + padding-left: 24px; + background-color: var(--bg-root); + color: var(--text-primary); + -webkit-font-smoothing: antialiased; + display: flex; + flex-direction: column; + min-height: 100vh; + box-sizing: border-box; + transition: + background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), + color 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} + +.frontmatter-container { + margin-bottom: 2rem; + padding-bottom: 1.5rem; +} + +.frontmatter-row { + display: flex; + align-items: baseline; + margin-bottom: 0.5rem; + font-size: 0.95em; +} + +.frontmatter-label { + width: 140px; + color: var(--text-tertiary); + flex-shrink: 0; +} + +.frontmatter-value { + color: var(--text-primary); + flex: 1; +} + +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: var(--text-primary); + margin-top: 2.5em; + margin-bottom: 0.75em; + line-height: 1.2; + letter-spacing: -0.02em; + font-weight: 600; +} + +.content h1:first-child { + margin-top: 0; +} + +.content h1 { + font-size: 2em; + letter-spacing: -0.03em; + border-bottom: 1px solid var(--border-subtle); + padding-bottom: 0.5em; +} + +.content h2 { + font-size: 1.5em; +} + +.content h3, +.content h4, +.content h5, +.content h6 { + font-size: 1.25em; +} + +.content p { + margin-bottom: 1.5em; + color: var(--text-primary); + opacity: 0.95; +} + +.content code { + background-color: var(--code-bg); + padding: 2px 5px; + border-radius: 4px; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; + font-size: 0.9em; + color: var(--primary-color); + border: 1px solid var(--code-border); +} + +.content ul, +.content ol { + margin-bottom: 1.5em; + padding-left: 1.5em; + color: var(--text-tertiary); +} + +.content ul ul, +.content ul ol, +.content ol ul, +.content ol ol { + margin-top: 0.5em; + margin-bottom: 0; +} + +.content li { + margin-bottom: 0.5em; + color: var(--text-primary); +} + +.content li p { + margin: 0; +} + +.content a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: border-color 0.2s; +} + +.content a:hover { + border-color: var(--primary-color); +} + +.content blockquote { + border-left: 3px solid var(--primary-color); + margin: 0 0 1.5em 0; + padding-left: 1.2em; + color: var(--text-tertiary); + font-style: italic; +} + +.content table { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border-collapse: collapse; + margin-bottom: 1.5em; + font-size: 0.95em; +} + +.content th, +.content td { + padding: 10px; + border-bottom: 1px solid var(--border-subtle); + text-align: left; + white-space: nowrap; +} + +.content th { + color: var(--text-primary); + font-weight: 600; +} + +.content img { + max-width: 100%; + border-radius: 8px; + margin-bottom: 1.5em; +} + +.content hr { + border: none; + border-top: 1px solid var(--border-subtle); + margin: 3em 0; +} + +.content li:has(input[type="checkbox"]), +.content li.task-list-item { + list-style-type: none; +} + +.content ul:has(li input[type="checkbox"]) { + padding-left: 0; +} + +.content input[type="checkbox"] { + margin-left: 0; +} + +footer { + margin-top: 4rem; + padding-top: 2rem; + text-align: center; + font-size: 0.875rem; + color: var(--text-tertiary); +} + +footer p { + margin: 0; +} + +footer a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s; +} + +footer a:hover { + color: var(--text-primary); +} + +.top-actions button { + display: flex; + align-items: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.top-actions button:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.top-actions button svg { + width: 16px; + height: 16px; +} + +.export-btn { + display: flex; + align-items: center; + padding: 8px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.export-btn:hover { + background-color: var(--bg-card); + border-color: var(--border-subtle); + opacity: 1; +} + +.export-btn svg { + width: 18px; + height: 18px; +} + +.export-dropdown { + position: absolute; + top: calc(100% + 4px); + right: 0; + background-color: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: 8px; + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.06), + 0 12px 24px rgba(0, 0, 0, 0.08); + min-width: 180px; + padding: 6px; + opacity: 0; + visibility: hidden; + transform: translateY(-8px) scale(0.96); + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 100; + transform-origin: top right; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.export-dropdown.show { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); +} + +.export-option { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 7px 10px; + background: transparent; + border: none; + border-radius: 6px; + text-align: left; + cursor: pointer; + font-size: 13px; + font-weight: 500; + color: var(--text-primary); + transition: all 0.1s ease; + position: relative; +} + +.export-option:hover { + background-color: rgba(94, 106, 210, 0.1); + color: var(--primary-color); +} + +.export-option .option-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + opacity: 0.6; + stroke-width: 2; + transition: opacity 0.1s ease; +} + +.export-option:hover .option-icon { + opacity: 1; +} + +.export-option .option-check { + width: 16px; + height: 16px; + flex-shrink: 0; + display: none; + stroke-width: 2.5; +} + +/* Source: theme/features/default.css */ +.toc-toggle { + position: fixed; + top: 16px; + left: 16px; + z-index: 140; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px; + background-color: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + opacity: 0.7; + transition: all 0.2s ease; +} + +.toc-toggle:hover { + background-color: var(--bg-card); + opacity: 1; +} + +.toc-toggle svg { + width: 16px; + height: 16px; + color: inherit; +} + +.toc-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(2px); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 110; +} + +.toc-panel { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 280px; + padding: 24px; + padding-top: 68px; + background-color: var(--bg-root); + border-right: 1px solid var(--border-subtle); + transform: translateX(-100%); + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); + overflow-y: auto; + z-index: 130; + opacity: 0; +} + +.toc-header { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-tertiary); + margin-bottom: 16px; + padding-left: 8px; +} + +.toc-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; +} + +.toc-item { + margin: 0; + position: relative; +} + +.toc-link { + display: block; + padding: 6px 8px; + color: var(--text-tertiary); + text-decoration: none; + font-size: 13px; + line-height: 1.5; + transition: all 0.15s ease; + border-radius: 6px; + position: relative; +} + +.toc-link:hover { + color: var(--text-primary); + background-color: var(--bg-card); +} + +.toc-item[data-level="2"] .toc-link { + padding-left: 8px; +} + +.toc-item[data-level="3"] .toc-link { + padding-left: 20px; +} + +.toc-item[data-level="4"] .toc-link { + padding-left: 32px; +} + +.toc-item[data-level="5"] .toc-link { + padding-left: 44px; +} + +.toc-item[data-level="6"] .toc-link { + padding-left: 56px; +} + +body.toc-open .toc-backdrop { + opacity: 1; + visibility: visible; +} + +body.toc-open .toc-panel { + transform: translateX(0); + opacity: 1; +} + +.theme-toggle .icon-sun { + display: none; +} + +.theme-toggle .icon-moon { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-sun { + display: block; +} + +:root[data-theme="dark"] .theme-toggle .icon-moon { + display: none; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .theme-toggle .icon-sun { + display: block; + } + + :root:not([data-theme="light"]) .theme-toggle .icon-moon { + display: none; + } +} + +:root[data-theme="dark"] .export-dropdown { + background-color: rgba(22, 23, 26, 0.9); + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.2), + 0 12px 24px rgba(0, 0, 0, 0.4); +} + +:root[data-theme="dark"] .export-option:hover { + background-color: rgba(127, 138, 239, 0.15); + color: var(--primary-color); +} + +@media print { + .toc-toggle, + .toc-panel, + .toc-backdrop { + display: none; + } +} diff --git a/public/themes/everforest.hljs.css b/public/themes/everforest.hljs.css new file mode 100644 index 0000000..dc623cd --- /dev/null +++ b/public/themes/everforest.hljs.css @@ -0,0 +1,205 @@ +/* Generated highlight theme bundle for "everforest". Do not edit directly. */ + +/* Source: hljs/base.css */ +pre { + background-color: var(--code-bg); + border: 1px solid var(--code-border); + overflow-x: auto; + margin-bottom: 1.5em; +} + +pre code, +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; + background-color: transparent; + color: inherit; + border: none; + font-size: 0.85em; + font-family: "SF Mono", "Menlo", "Monaco", "Courier New", monospace; +} + +/* Source: hljs/variants/everforest.css */ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #f4f0d9; + color: #5c6a72; +} + +.hljs-comment, +.hljs-quote { + color: #939f91; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #f57d26; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #3a94c5; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #dfa000; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #879a39; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #df69ba; +} + +.hljs-deletion { + color: #f85552; + display: inline-block; + width: 100%; + background-color: rgba(248, 85, 82, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #232a2e; + color: #d3c6aa; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #859289; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #e67e80; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #7fbbb3; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #db3c26; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #a7c080; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #d699b6; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #e67e80; + background-color: rgba(230, 126, 128, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #232a2e; + color: #d3c6aa; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #859289; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #e67e80; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #7fbbb3; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #db3c26; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #a7c080; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #d699b6; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #e67e80; + background-color: rgba(230, 126, 128, 0.15); +} diff --git a/shared/templates/template-hash.generated.ts b/shared/templates/template-hash.generated.ts index 7c4d80b..c80af05 100644 --- a/shared/templates/template-hash.generated.ts +++ b/shared/templates/template-hash.generated.ts @@ -1,6 +1,6 @@ // This file is auto-generated by scripts/generate-template-hash.ts // Do not edit manually. Run "pnpm generate:hash" to regenerate. -export const TEMPLATE_HASH = "0bdd6"; +export const TEMPLATE_HASH = "a162b"; export function getTemplateHash(): string { return TEMPLATE_HASH; diff --git a/shared/themes/source/hljs/variants/catppuccin.css b/shared/themes/source/hljs/variants/catppuccin.css new file mode 100644 index 0000000..327ba96 --- /dev/null +++ b/shared/themes/source/hljs/variants/catppuccin.css @@ -0,0 +1,182 @@ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #e6e9ef; + color: #4c4f69; +} + +.hljs-comment, +.hljs-quote { + color: #9ca0b0; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #8839ef; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #1e66f5; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #df8e1d; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #40a02b; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #fe640b; +} + +.hljs-deletion { + color: #d20f39; + display: inline-block; + width: 100%; + background-color: rgba(210, 15, 57, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #181825; + color: #cdd6f4; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #6c7086; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #cba6f7; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #89b4fa; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #f9e2af; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #a6e3a1; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #fab387; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #f38ba8; + background-color: rgba(243, 139, 168, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #181825; + color: #cdd6f4; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #6c7086; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #cba6f7; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #89b4fa; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #f9e2af; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #a6e3a1; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #fab387; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #f38ba8; + background-color: rgba(243, 139, 168, 0.15); +} diff --git a/shared/themes/source/hljs/variants/dracula.css b/shared/themes/source/hljs/variants/dracula.css new file mode 100644 index 0000000..8876e90 --- /dev/null +++ b/shared/themes/source/hljs/variants/dracula.css @@ -0,0 +1,182 @@ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #e9e9f4; + color: #282a36; +} + +.hljs-comment, +.hljs-quote { + color: #6272a4; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #bd93f9; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #0073e6; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #b45309; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #15803d; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #c2410c; +} + +.hljs-deletion { + color: #ff5555; + display: inline-block; + width: 100%; + background-color: rgba(255, 85, 85, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #21222c; + color: #f8f8f2; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #6272a4; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #ff79c6; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #bd93f9; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #8be9fd; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #f1fa8c; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #ffb86c; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #ff5555; + background-color: rgba(255, 85, 85, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #21222c; + color: #f8f8f2; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #6272a4; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #ff79c6; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #bd93f9; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #8be9fd; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #f1fa8c; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #ffb86c; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #ff5555; + background-color: rgba(255, 85, 85, 0.15); +} diff --git a/shared/themes/source/hljs/variants/everforest.css b/shared/themes/source/hljs/variants/everforest.css new file mode 100644 index 0000000..b1efbf5 --- /dev/null +++ b/shared/themes/source/hljs/variants/everforest.css @@ -0,0 +1,182 @@ +code.hljs { + padding: 3px 5px; +} + +.hljs { + background: #f4f0d9; + color: #5c6a72; +} + +.hljs-comment, +.hljs-quote { + color: #939f91; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-name { + color: #f57d26; + font-weight: bold; +} + +.hljs-attribute, +.hljs-meta { + color: #3a94c5; +} + +.hljs-class .hljs-title, +.hljs-title.class_, +.hljs-type, +.hljs-section, +.hljs-function .hljs-title { + color: #dfa000; +} + +.hljs-string, +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #879a39; +} + +.hljs-bullet, +.hljs-code, +.hljs-addition, +.hljs-number, +.hljs-built_in, +.hljs-params { + color: #df69ba; +} + +.hljs-deletion { + color: #f85552; + display: inline-block; + width: 100%; + background-color: rgba(248, 85, 82, 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .hljs { + background: #232a2e; + color: #d3c6aa; + } + + :root:not([data-theme="light"]) .hljs-comment, + :root:not([data-theme="light"]) .hljs-quote { + color: #859289; + } + + :root:not([data-theme="light"]) .hljs-keyword, + :root:not([data-theme="light"]) .hljs-selector-tag, + :root:not([data-theme="light"]) .hljs-literal, + :root:not([data-theme="light"]) .hljs-name { + color: #e67e80; + } + + :root:not([data-theme="light"]) .hljs-attribute, + :root:not([data-theme="light"]) .hljs-meta { + color: #7fbbb3; + } + + :root:not([data-theme="light"]) .hljs-class .hljs-title, + :root:not([data-theme="light"]) .hljs-title.class_, + :root:not([data-theme="light"]) .hljs-type, + :root:not([data-theme="light"]) .hljs-section, + :root:not([data-theme="light"]) .hljs-function .hljs-title { + color: #db3c26; + } + + :root:not([data-theme="light"]) .hljs-string, + :root:not([data-theme="light"]) .hljs-regexp, + :root:not([data-theme="light"]) .hljs-symbol, + :root:not([data-theme="light"]) .hljs-variable, + :root:not([data-theme="light"]) .hljs-template-variable, + :root:not([data-theme="light"]) .hljs-link, + :root:not([data-theme="light"]) .hljs-selector-attr, + :root:not([data-theme="light"]) .hljs-selector-pseudo { + color: #a7c080; + } + + :root:not([data-theme="light"]) .hljs-bullet, + :root:not([data-theme="light"]) .hljs-code, + :root:not([data-theme="light"]) .hljs-addition, + :root:not([data-theme="light"]) .hljs-number, + :root:not([data-theme="light"]) .hljs-built_in, + :root:not([data-theme="light"]) .hljs-params { + color: #d699b6; + } + + :root:not([data-theme="light"]) .hljs-deletion { + color: #e67e80; + background-color: rgba(230, 126, 128, 0.15); + } +} + +:root[data-theme="dark"] .hljs { + background: #232a2e; + color: #d3c6aa; +} + +:root[data-theme="dark"] .hljs-comment, +:root[data-theme="dark"] .hljs-quote { + color: #859289; +} + +:root[data-theme="dark"] .hljs-keyword, +:root[data-theme="dark"] .hljs-selector-tag, +:root[data-theme="dark"] .hljs-literal, +:root[data-theme="dark"] .hljs-name { + color: #e67e80; +} + +:root[data-theme="dark"] .hljs-attribute, +:root[data-theme="dark"] .hljs-meta { + color: #7fbbb3; +} + +:root[data-theme="dark"] .hljs-class .hljs-title, +:root[data-theme="dark"] .hljs-title.class_, +:root[data-theme="dark"] .hljs-type, +:root[data-theme="dark"] .hljs-section, +:root[data-theme="dark"] .hljs-function .hljs-title { + color: #db3c26; +} + +:root[data-theme="dark"] .hljs-string, +:root[data-theme="dark"] .hljs-regexp, +:root[data-theme="dark"] .hljs-symbol, +:root[data-theme="dark"] .hljs-variable, +:root[data-theme="dark"] .hljs-template-variable, +:root[data-theme="dark"] .hljs-link, +:root[data-theme="dark"] .hljs-selector-attr, +:root[data-theme="dark"] .hljs-selector-pseudo { + color: #a7c080; +} + +:root[data-theme="dark"] .hljs-bullet, +:root[data-theme="dark"] .hljs-code, +:root[data-theme="dark"] .hljs-addition, +:root[data-theme="dark"] .hljs-number, +:root[data-theme="dark"] .hljs-built_in, +:root[data-theme="dark"] .hljs-params { + color: #d699b6; +} + +:root[data-theme="dark"] .hljs-deletion { + color: #e67e80; + background-color: rgba(230, 126, 128, 0.15); +} diff --git a/shared/themes/source/theme/tokens/catppuccin.css b/shared/themes/source/theme/tokens/catppuccin.css new file mode 100644 index 0000000..0a8b94a --- /dev/null +++ b/shared/themes/source/theme/tokens/catppuccin.css @@ -0,0 +1,40 @@ +:root { + --bg-root: #eff1f5; + --bg-card: #e6e9ef; + --border-subtle: #bcc0cc; + --text-primary: #4c4f69; + --text-secondary: #1e66f5; + --text-tertiary: #6c6f85; + --primary-color: #8839ef; + --code-bg: #e6e9ef; + --code-border: #ccd0da; + --shadow-card: 0 1px 2px rgba(76, 79, 105, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #1e1e2e; + --bg-card: #181825; + --border-subtle: #45475a; + --text-primary: #cdd6f4; + --text-secondary: #89b4fa; + --text-tertiary: #a6adc8; + --primary-color: #cba6f7; + --code-bg: #181825; + --code-border: #313244; + --shadow-card: 0 2px 6px rgba(17, 17, 27, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #1e1e2e; + --bg-card: #181825; + --border-subtle: #45475a; + --text-primary: #cdd6f4; + --text-secondary: #89b4fa; + --text-tertiary: #a6adc8; + --primary-color: #cba6f7; + --code-bg: #181825; + --code-border: #313244; + --shadow-card: 0 2px 6px rgba(17, 17, 27, 0.35); +} diff --git a/shared/themes/source/theme/tokens/dracula.css b/shared/themes/source/theme/tokens/dracula.css new file mode 100644 index 0000000..d8f69b6 --- /dev/null +++ b/shared/themes/source/theme/tokens/dracula.css @@ -0,0 +1,40 @@ +:root { + --bg-root: #f8f8f2; + --bg-card: #e9e9f4; + --border-subtle: #d6d6e5; + --text-primary: #282a36; + --text-secondary: #bd93f9; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #e9e9f4; + --code-border: #d6d6e5; + --shadow-card: 0 1px 2px rgba(40, 42, 54, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #282a36; + --bg-card: #21222c; + --border-subtle: #44475a; + --text-primary: #f8f8f2; + --text-secondary: #8be9fd; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #21222c; + --code-border: #44475a; + --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #282a36; + --bg-card: #21222c; + --border-subtle: #44475a; + --text-primary: #f8f8f2; + --text-secondary: #8be9fd; + --text-tertiary: #6272a4; + --primary-color: #bd93f9; + --code-bg: #21222c; + --code-border: #44475a; + --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.35); +} diff --git a/shared/themes/source/theme/tokens/everforest.css b/shared/themes/source/theme/tokens/everforest.css new file mode 100644 index 0000000..55e18e2 --- /dev/null +++ b/shared/themes/source/theme/tokens/everforest.css @@ -0,0 +1,40 @@ +:root { + --bg-root: #fdf6e3; + --bg-card: #f4f0d9; + --border-subtle: #e0dcc7; + --text-primary: #5c6a72; + --text-secondary: #879a39; + --text-tertiary: #939f91; + --primary-color: #879a39; + --code-bg: #f4f0d9; + --code-border: #e0dcc7; + --shadow-card: 0 1px 2px rgba(92, 106, 114, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-root: #2b3339; + --bg-card: #323c41; + --border-subtle: #414b50; + --text-primary: #d3c6aa; + --text-secondary: #a7c080; + --text-tertiary: #859289; + --primary-color: #a7c080; + --code-bg: #232a2e; + --code-border: #3a454a; + --shadow-card: 0 2px 6px rgba(26, 31, 35, 0.35); + } +} + +:root[data-theme="dark"] { + --bg-root: #2b3339; + --bg-card: #323c41; + --border-subtle: #414b50; + --text-primary: #d3c6aa; + --text-secondary: #a7c080; + --text-tertiary: #859289; + --primary-color: #a7c080; + --code-bg: #232a2e; + --code-border: #3a454a; + --shadow-card: 0 2px 6px rgba(26, 31, 35, 0.35); +} diff --git a/shared/themes/theme-registry.spec.ts b/shared/themes/theme-registry.spec.ts index 0836ddc..2fe11b3 100644 --- a/shared/themes/theme-registry.spec.ts +++ b/shared/themes/theme-registry.spec.ts @@ -24,6 +24,9 @@ describe("theme registry", () => { { value: "resume", label: "Resume" }, { value: "github", label: "GitHub" }, { value: "matrix", label: "Matrix" }, + { value: "catppuccin", label: "Catppuccin" }, + { value: "everforest", label: "Everforest" }, + { value: "dracula", label: "Dracula" }, ]); }); @@ -35,6 +38,9 @@ describe("theme registry", () => { ]); expect(OVERFLOW_THEME_OPTIONS).toEqual([ { value: "matrix", label: "Matrix" }, + { value: "catppuccin", label: "Catppuccin" }, + { value: "everforest", label: "Everforest" }, + { value: "dracula", label: "Dracula" }, ]); }); @@ -51,5 +57,17 @@ describe("theme registry", () => { showToc: false, showColorModeToggle: false, }); + expect(getThemeDefinition("catppuccin").features).toEqual({ + showToc: true, + showColorModeToggle: true, + }); + expect(getThemeDefinition("everforest").features).toEqual({ + showToc: true, + showColorModeToggle: true, + }); + expect(getThemeDefinition("dracula").features).toEqual({ + showToc: true, + showColorModeToggle: true, + }); }); }); diff --git a/shared/themes/theme-registry.ts b/shared/themes/theme-registry.ts index 14b320b..58654f6 100644 --- a/shared/themes/theme-registry.ts +++ b/shared/themes/theme-registry.ts @@ -1,4 +1,12 @@ -export const THEME_IDS = ["default", "resume", "github", "matrix"] as const; +export const THEME_IDS = [ + "default", + "resume", + "github", + "matrix", + "catppuccin", + "everforest", + "dracula", +] as const; export type ThemeId = (typeof THEME_IDS)[number]; @@ -87,6 +95,51 @@ export const THEMES: Record = { showColorModeToggle: false, }, }, + catppuccin: { + id: "catppuccin", + label: "Catppuccin", + themeFragments: [ + "theme/tokens/catppuccin.css", + "theme/base.css", + "theme/presets/article.css", + "theme/features/default.css", + ], + hljsFragments: ["hljs/base.css", "hljs/variants/catppuccin.css"], + features: { + showToc: true, + showColorModeToggle: true, + }, + }, + everforest: { + id: "everforest", + label: "Everforest", + themeFragments: [ + "theme/tokens/everforest.css", + "theme/base.css", + "theme/presets/article.css", + "theme/features/default.css", + ], + hljsFragments: ["hljs/base.css", "hljs/variants/everforest.css"], + features: { + showToc: true, + showColorModeToggle: true, + }, + }, + dracula: { + id: "dracula", + label: "Dracula", + themeFragments: [ + "theme/tokens/dracula.css", + "theme/base.css", + "theme/presets/article.css", + "theme/features/default.css", + ], + hljsFragments: ["hljs/base.css", "hljs/variants/dracula.css"], + features: { + showToc: true, + showColorModeToggle: true, + }, + }, }; export function getThemeDefinition(value?: string | null): ThemeDefinition {