diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..6e1432a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + }, + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], +}; diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 91d74f7..1c54956 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -1,6 +1,6 @@ services: halo: - image: registry.fit2cloud.com/halo/halo:2.22.12 + image: registry.fit2cloud.com/halo/halo:2.24.2 volumes: - ./data:/root/.halo2 - ../:/root/.halo2/themes/theme-terminal diff --git a/i18n/default.properties b/i18n/default.properties index e80419b..e7572db 100644 --- a/i18n/default.properties +++ b/i18n/default.properties @@ -6,3 +6,71 @@ page.tag.title=Tag: {0} page.categories.title=Categories page.category.title=Category: {0} page.about.title=About +devcm.activity.aria=IDE activity bar +devcm.activity.link=Link +devcm.activity.explorer=Explorer +devcm.activity.search=Search +devcm.activity.archive=Archive +devcm.activity.categories=Categories +devcm.activity.tags=Tags +devcm.activity.login=Login +devcm.activity.console=Console +devcm.activity.logout=Logout +devcm.action.open=Open +devcm.action.openPage=Open page +devcm.action.openCategory=Open category +devcm.action.closeTab=Close editor tab +devcm.action.closePostTab=Close post tab +devcm.action.closePageTab=Close page tab +devcm.action.closeCategoryTab=Close category tab +devcm.action.closeTagTab=Close tag tab +devcm.action.closeTagsTab=Close tags tab +devcm.action.closeLinksTab=Close links tab +devcm.action.closeMomentsTab=Close moments tab +devcm.action.newPost=New post +devcm.action.toggleTheme=Toggle theme +devcm.home.subtitle=Development Content Management. +devcm.home.defaultContent=A Terminal-style Halo theme. +devcm.home.postCount=> Current page has {0} post(s) +devcm.post.featured=FEATURED +devcm.post.readMore=Read more +devcm.pagination.previous=Previous +devcm.pagination.next=Next +devcm.explorer.title=EXPLORER +devcm.explorer.pages=pages +devcm.explorer.home=Home +devcm.explorer.categories=CATEGORIES +devcm.explorer.tags=TAGS CLOUD +devcm.right.context=Context panel +devcm.right.articleInfo=ARTICLE INFO +devcm.right.author=Author +devcm.right.publishedAt=Published at +devcm.right.category=Category +devcm.right.tags=Tags +devcm.right.toc=TABLE OF CONTENTS +devcm.right.nearbyPosts=NEARBY POSTS +devcm.right.previousPost=Previous +devcm.right.nextPost=Next +devcm.right.latestPosts=LATEST POSTS +devcm.right.posts=posts +devcm.archive.command=archive --view posts +devcm.archive.title=Archives +devcm.archive.description=Browse content by publish date. +devcm.archive.tableTitle=Archive list +devcm.archive.columnTitle=Title +devcm.archive.columnTags=Tags +devcm.archive.columnDate=Date +devcm.archive.group=Archive group +devcm.categories.command=archive --view categories +devcm.categories.title=Categories +devcm.categories.description=Browse posts by category and explore the site structure. +devcm.categories.articleCount={0} articles +devcm.tags.title=All tags +devcm.taxonomy.categoryTitle=Category: {0} +devcm.taxonomy.tagTitle=Tag: {0} +devcm.taxonomy.postCount=({0} posts) +devcm.links.defaultGroup=Links +devcm.comments.file=comments.log +devcm.comments.title=## Comments +devcm.status.poweredBy=Powered by +devcm.status.language=Language diff --git a/i18n/zh.properties b/i18n/zh.properties index 9684d3b..a602606 100644 --- a/i18n/zh.properties +++ b/i18n/zh.properties @@ -6,3 +6,71 @@ page.tag.title=标签:{0} page.categories.title=分类 page.category.title=分类:{0} page.about.title=关于 +devcm.activity.aria=IDE 活动栏 +devcm.activity.link=链接 +devcm.activity.explorer=资源管理器 +devcm.activity.search=搜索 +devcm.activity.archive=归档 +devcm.activity.categories=分类 +devcm.activity.tags=标签 +devcm.activity.login=登录 +devcm.activity.console=控制台 +devcm.activity.logout=登出 +devcm.action.open=打开 +devcm.action.openPage=打开页面 +devcm.action.openCategory=打开分类 +devcm.action.closeTab=关闭编辑器标签 +devcm.action.closePostTab=关闭文章标签 +devcm.action.closePageTab=关闭页面标签 +devcm.action.closeCategoryTab=关闭分类标签 +devcm.action.closeTagTab=关闭标签页 +devcm.action.closeTagsTab=关闭标签列表 +devcm.action.closeLinksTab=关闭链接页 +devcm.action.closeMomentsTab=关闭瞬间页 +devcm.action.newPost=新建文章 +devcm.action.toggleTheme=切换主题 +devcm.home.subtitle=Development Content Management. +devcm.home.defaultContent=一款 Terminal 风格的 Halo 主题。 +devcm.home.postCount=> 当前页共有 {0} 篇文章 +devcm.post.featured=精选 +devcm.post.readMore=阅读全文 +devcm.pagination.previous=上一页 +devcm.pagination.next=下一页 +devcm.explorer.title=资源管理器 +devcm.explorer.pages=pages +devcm.explorer.home=首页 +devcm.explorer.categories=分类 +devcm.explorer.tags=标签云 +devcm.right.context=上下文面板 +devcm.right.articleInfo=文章信息 +devcm.right.author=作者 +devcm.right.publishedAt=发布于 +devcm.right.category=分类 +devcm.right.tags=标签 +devcm.right.toc=目录 +devcm.right.nearbyPosts=相邻文章 +devcm.right.previousPost=上一篇 +devcm.right.nextPost=下一篇 +devcm.right.latestPosts=最新文章 +devcm.right.posts=篇文章 +devcm.archive.command=archive --view posts +devcm.archive.title=文章归档 +devcm.archive.description=按发布时间浏览站点内容。 +devcm.archive.tableTitle=归档列表 +devcm.archive.columnTitle=标题 +devcm.archive.columnTags=标签 +devcm.archive.columnDate=日期 +devcm.archive.group=归档分组 +devcm.categories.command=archive --view categories +devcm.categories.title=分类 +devcm.categories.description=按分类浏览文章,探索站点内容结构。 +devcm.categories.articleCount={0} 篇文章 +devcm.tags.title=所有标签 +devcm.taxonomy.categoryTitle=分类:{0} +devcm.taxonomy.tagTitle=标签:{0} +devcm.taxonomy.postCount=({0} 篇文章) +devcm.links.defaultGroup=友情链接 +devcm.comments.file=comments.log +devcm.comments.title=## 评论区 +devcm.status.poweredBy=Powered by +devcm.status.language=语言 diff --git a/package.json b/package.json index 73c83ca..b6b3b00 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "theme-terminal", "private": true, - "version": "1.2.7", + "version": "2.0.3", "description": "A terminal like theme for Halo.", "scripts": { "dev": "vite build --watch", "build": "tsc && vite build && theme-package", "build-only": "tsc && vite build", "lint": "eslint ./src --ext .js,.cjs,.mjs,.ts,.cts,.mts --ignore-path .gitignore", + "test:structure": "node scripts/check-devcm-theme.mjs", "prettier": "prettier --write './src/**/*.{js,ts,css,json,ml,yaml,html}' './templates/**/*.html'" }, "keywords": [ @@ -51,4 +52,4 @@ "@iconify/iconify": "^3.0.0", "alpinejs": "^3.10.5" } -} \ No newline at end of file +} diff --git a/settings.yaml b/settings.yaml index 32afca4..f9ca1e8 100644 --- a/settings.yaml +++ b/settings.yaml @@ -22,30 +22,289 @@ spec: label: logo图片链接 help: 配置后,将展示在导航栏左侧。 accepts: - - "image/png" + - "image/png" - $formkit: text name: title label: 站点标题 help: 配置后,将展示站点标题。 - value: 'Terminal' + value: "Terminal" - group: index label: 首页设置 formSchema: - $formkit: text name: index_notice_title label: 首页公告标题 - value: 'Hello Halo!' + value: "Hello Halo!" - $formkit: textarea name: index_notice_content label: 首页公告内容 - value: '一款 Terminal 风格的 Halo 主题。' + value: "A Terminal-style Halo theme." + - group: activity + label: 活动栏 + formSchema: + - $formkit: array + name: activity_items + label: 导航入口 + help: 按列表顺序渲染左侧 Activity Bar;可拖拽调整顺序。内置入口会自动处理图标、链接和激活状态,自定义入口才需要填写链接地址。 + itemLabels: + - type: text + label: $value.label + - type: text + label: $value.type + value: + - enabled: true + type: home + position: up + label: 首页 + icon: folder + url: / + external: false + - enabled: true + type: search + position: up + label: 搜索 + icon: search + url: "" + external: false + - enabled: true + type: archive + position: up + label: 归档 + icon: archive + url: /archives + external: false + - enabled: true + type: categories + position: up + label: 分类 + icon: script-text + url: /categories + external: false + - enabled: true + type: tags + position: up + label: 标签 + icon: label + url: /tags + external: false + - enabled: true + type: login + position: down + label: 登录 + icon: user + url: /console + external: false + children: + - $formkit: switch + name: enabled + label: 启用入口 + value: true + - $formkit: select + name: type + label: 入口类型 + value: custom + options: + - label: 首页 + value: home + - label: 搜索 + value: search + - label: 归档 + value: archive + - label: 分类 + value: categories + - label: 标签 + value: tags + - label: 登录 + value: login + - label: 自定义 + value: custom + - $formkit: text + name: label + label: 显示名称 + help: 用于鼠标悬浮提示和无障碍标签;建议自定义入口必填。 + - $formkit: select + name: icon + label: 图标 + value: file + help: 内置入口已有默认图标;自定义入口可在这里选择同风格图标。 + options: + - label: 文件夹 + value: folder + - label: 首页 + value: home + - label: 搜索 + value: search + - label: 归档 + value: archive + - label: 文档 + value: script-text + - label: 文章 + value: article + - label: 打开的书 + value: book-open + - label: 文件 + value: file + - label: 文件变体 + value: file-alt + - label: 多文件 + value: file-multiple + - label: 便签 + value: note + - label: 多便签 + value: notes + - label: 标签 + value: label + - label: 标签变体 + value: label-alt + - label: 用户 + value: user + - label: 用户组 + value: users + - label: 联系人 + value: contact + - label: Git 分支 + value: git-branch + - label: Git 提交 + value: git-commit + - label: Git 合并 + value: git-merge + - label: Pull Request + value: git-pull-request + - label: GitHub + value: github + - label: 链接 + value: link + - label: 外链 + value: external-link + - label: 打开 + value: open + - label: 代码 + value: code + - label: 命令 + value: command + - label: 缺陷 + value: bug + - label: 调试 + value: debug + - label: 服务器 + value: server + - label: 云 + value: cloud + - label: 显示器 + value: monitor + - label: 笔记本 + value: device-laptop + - label: 仪表盘 + value: dashboard + - label: 分析 + value: analytics + - label: 图表 + value: chart + - label: 柱状图 + value: chart-bar + - label: 看板 + value: kanban + - label: 时间线 + value: timeline + - label: 邮件 + value: mail + - label: 消息 + value: message + - label: 聊天 + value: chat + - label: 评论 + value: comment + - label: 通知 + value: notification + - label: 日历 + value: calendar + - label: 时钟 + value: clock + - label: 书签 + value: bookmark + - label: 收藏 + value: heart + - label: 灵感 + value: lightbulb + - label: 防护 + value: shield + - label: 锁 + value: lock + - label: 图片 + value: image + - label: 图库 + value: image-gallery + - label: 相机 + value: camera + - label: 音乐 + value: music + - label: 视频 + value: video + - label: 购物袋 + value: shopping-bag + - label: 购物车 + value: cart + - label: 钱包 + value: wallet + - label: 地图 + value: map + - label: 图钉 + value: pin + - label: 旗帜 + value: flag + - label: 电源 + value: power + - label: 上传 + value: upload + - label: 下载 + value: download + - label: 编辑 + value: edit + - label: 删除 + value: trash + - label: 加号 + value: plus + - label: 关闭 + value: close + - label: 滑杆 + value: sliders + - label: 滑杆 2 + value: sliders-2 + - label: 可见 + value: visible + - label: 查看 + value: eye + - label: 隐藏 + value: eye-closed + - $formkit: text + name: url + label: 链接地址 + help: 搜索入口可留空;其他入口留空时不会跳转。 + - $formkit: select + name: position + label: 所在位置 + value: up + options: + - label: 上方 + value: up + - label: 下方 + value: down + - $formkit: select + name: external + label: 打开方式 + value: false + options: + - label: 当前窗口 + value: false + - label: 新窗口 + value: true - group: footer label: 页脚设置 formSchema: - $formkit: text name: footer_powered_text label: 页脚文本 - value: 'FillCode' + value: "FillCode" - $formkit: text name: footer_powered_link label: 页脚文本链接 @@ -64,4 +323,3 @@ spec: name: footer_gongan_link label: 公安备案跳转链接 value: https://www.beian.gov.cn/ - diff --git a/src/alpine/activity-actions.ts b/src/alpine/activity-actions.ts new file mode 100644 index 0000000..852b5cc --- /dev/null +++ b/src/alpine/activity-actions.ts @@ -0,0 +1,24 @@ +declare global { + interface Window { + SearchWidget: { open: (() => void) | undefined } | undefined; + } +} + +export const initDevcmActivityActions = () => { + document.addEventListener("click", (event) => { + const target = event.target; + + if (!(target instanceof Element)) { + return; + } + + const actionTrigger = target.closest('[data-devcm-action="search"]'); + + if (!actionTrigger) { + return; + } + + event.preventDefault(); + window.SearchWidget?.open?.(); + }); +}; diff --git a/src/alpine/header-dividing.ts b/src/alpine/header-dividing.ts deleted file mode 100644 index a164afa..0000000 --- a/src/alpine/header-dividing.ts +++ /dev/null @@ -1,35 +0,0 @@ -export const headerDividing = () => ({ - isSticky: false, - $el: null as HTMLElement | null, - _observer: null as IntersectionObserver | null, - - init() { - const centerEl = this.$el as HTMLElement; - if (!centerEl) return; - - const header = centerEl.closest('.header') as HTMLElement; - - if (!header) return; - - // 直接观察 header 元素本身 - this._observer = new IntersectionObserver( - (entries) => { - const entry = entries[0]; - this.isSticky = entry.boundingClientRect.top < 0 && !entry.isIntersecting; - }, - { - threshold: 0, // 只要有任何部分离开就触发 - rootMargin: '0px' // 无偏移 - } - ); - - this._observer.observe(header); - }, - - destroy() { - if (this._observer) { - this._observer.disconnect(); - this._observer = null; - } - } -}); diff --git a/src/alpine/index.ts b/src/alpine/index.ts new file mode 100644 index 0000000..00b544a --- /dev/null +++ b/src/alpine/index.ts @@ -0,0 +1,26 @@ +import type { Alpine } from "alpinejs"; + +import { headerMenu } from "./header-menu"; +import { postLineNum } from "./post-line-num"; +import { postToc } from "./post-toc"; +import { themeComment } from "./theme-comment"; +import { themeMode } from "./theme-mode"; +import { upvote } from "./upvote"; +import { initDevcmActivityActions } from "./activity-actions"; +import { initTypedText } from "./typed-text"; + +export const bootDevcmAlpine = (Alpine: Alpine) => { + window.Alpine = Alpine; + + Alpine.data("upvote", upvote); + Alpine.data("themeMode", themeMode); + Alpine.data("postLineNum", postLineNum); + Alpine.data("postToc", postToc); + Alpine.data("headerMenu", headerMenu); + Alpine.data("themeComment", themeComment); + + initDevcmActivityActions(); + initTypedText(); + + Alpine.start(); +}; diff --git a/src/alpine/theme-comment.ts b/src/alpine/theme-comment.ts new file mode 100644 index 0000000..274abd6 --- /dev/null +++ b/src/alpine/theme-comment.ts @@ -0,0 +1,88 @@ +import commentWidgetThemeStyle from "../styles/devcm-comment-widget.css?raw"; + +const injectShadowStyle = (root: ShadowRoot | null | undefined, id: string, css: string) => { + if (!root || root.querySelector(`#${id}`)) { + return; + } + + const style = document.createElement("style"); + style.id = id; + style.textContent = css; + root.appendChild(style); +}; + +const themeCommentRoot = (root: ParentNode | null | undefined) => { + if (!root) { + return; + } + + root.querySelectorAll("*").forEach((element) => { + if (element.shadowRoot) { + injectShadowStyle(element.shadowRoot, "devcm-comment-widget-theme", commentWidgetThemeStyle); + themeCommentRoot(element.shadowRoot); + } + }); +}; + +const commentScopeSelector = ".devcm-comments, .devcm-moment-comments"; + +const getCommentScopes = (root: ParentNode) => { + const scopes = Array.from(root.querySelectorAll(commentScopeSelector)); + if (root instanceof HTMLElement && root.matches(commentScopeSelector)) { + scopes.unshift(root); + } + return scopes; +}; + +const themeCommentWidget = (root: ParentNode) => { + const commentScopes = getCommentScopes(root); + if (!commentScopes.length) { + return; + } + + commentScopes.forEach((comments) => { + themeCommentRoot(comments); + + comments.querySelectorAll("comment-widget").forEach((widget) => { + injectShadowStyle(widget.shadowRoot, "devcm-comment-widget-theme", commentWidgetThemeStyle); + + const form = widget.shadowRoot?.querySelector("comment-form"); + injectShadowStyle(form?.shadowRoot, "devcm-comment-form-theme", commentWidgetThemeStyle); + + const baseForm = form?.shadowRoot?.querySelector("base-form"); + injectShadowStyle(baseForm?.shadowRoot, "devcm-comment-base-form-theme", commentWidgetThemeStyle); + }); + }); +}; + +type ThemeCommentState = { + $el: HTMLElement; + observer: MutationObserver | null; + timers: number[]; + init: () => void; + destroy: () => void; + refresh: () => void; +}; + +export const themeComment = (): ThemeCommentState => ({ + $el: null as unknown as HTMLElement, + observer: null, + timers: [], + init() { + this.refresh(); + this.timers = [120, 500, 1200, 2400].map((delay) => window.setTimeout(() => this.refresh(), delay)); + + this.observer = new MutationObserver(() => this.refresh()); + this.observer.observe(this.$el, { + childList: true, + subtree: true, + }); + }, + destroy() { + this.observer?.disconnect(); + this.timers.forEach((timer) => window.clearTimeout(timer)); + }, + refresh() { + themeCommentWidget(this.$el); + }, +}); diff --git a/src/alpine/theme-mode.ts b/src/alpine/theme-mode.ts index 714e04e..dc8c37b 100644 --- a/src/alpine/theme-mode.ts +++ b/src/alpine/theme-mode.ts @@ -4,16 +4,16 @@ interface ThemeModeState { handleToggleThemeMode(): void; } -export const themeMode = ():ThemeModeState => ({ - storedTheme: '', +export const themeMode = (): ThemeModeState => ({ + storedTheme: "", init() { - const storedTheme = localStorage.getItem("theme-mode") || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); + const storedTheme = localStorage.getItem("theme-mode") || "dark"; if (!storedTheme) return; document.documentElement.setAttribute("data-color-scheme", storedTheme); - + this.storedTheme = storedTheme; }, @@ -21,9 +21,9 @@ export const themeMode = ():ThemeModeState => ({ const targetTheme = this.storedTheme === "dark" ? "light" : "dark"; document.documentElement.setAttribute("data-color-scheme", targetTheme); - + this.storedTheme = targetTheme; localStorage.setItem("theme-mode", targetTheme); }, -}); \ No newline at end of file +}); diff --git a/src/alpine/typed-text.ts b/src/alpine/typed-text.ts new file mode 100644 index 0000000..09b5ce3 --- /dev/null +++ b/src/alpine/typed-text.ts @@ -0,0 +1,50 @@ +const runWhenReady = (callback: () => void) => { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", callback, { once: true }); + return; + } + + callback(); +}; + +const typewriterEffect = (selector: string) => { + const typedTextContainer = document.querySelector(selector); + + if (!typedTextContainer) { + return; + } + + const text = typedTextContainer.innerText; + const followingPromptLine = typedTextContainer.parentElement?.querySelector( + ".devcm-prompt-line--after-typed" + ); + + typedTextContainer.innerText = ""; + typedTextContainer.classList.add("typed-text--typing"); + typedTextContainer.classList.remove("typed-text--complete"); + followingPromptLine?.classList.add("devcm-prompt-line--pending"); + followingPromptLine?.setAttribute("aria-hidden", "true"); + + let index = 0; + + const typeNextCharacter = () => { + if (index >= text.length) { + typedTextContainer.classList.remove("typed-text--typing"); + typedTextContainer.classList.add("typed-text--complete"); + followingPromptLine?.classList.remove("devcm-prompt-line--pending"); + followingPromptLine?.removeAttribute("aria-hidden"); + return; + } + + typedTextContainer.innerText += text.charAt(index); + index += 1; + + window.setTimeout(typeNextCharacter, Math.random() * 200 + 50); + }; + + typeNextCharacter(); +}; + +export const initTypedText = (selector = ".typed-text") => { + runWhenReady(() => typewriterEffect(selector)); +}; diff --git a/src/main.ts b/src/main.ts index d1d0ffe..4b22cc3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,24 +2,7 @@ import "./styles/style.scss"; import "./styles/font-hack.scss"; import "./styles/font-pixel.scss"; -import Alpine from 'alpinejs' -import {upvote} from './alpine/upvote' -import {themeMode} from './alpine/theme-mode' -import {postLineNum} from './alpine/post-line-num' -import {postToc} from './alpine/post-toc' -import {headerDividing} from './alpine/header-dividing' -import {headerMenu} from './alpine/header-menu' -import {typewriterEffect} from './utils' +import Alpine from "alpinejs"; +import { bootDevcmAlpine } from "./alpine"; -window.Alpine = Alpine - -Alpine.data('upvote', upvote) -Alpine.data('themeMode', themeMode) -Alpine.data('postLineNum', postLineNum) -Alpine.data('postToc', postToc) -Alpine.data('headerMenu', headerMenu) -Alpine.data('headerDividing', headerDividing) - -Alpine.start() - -document.addEventListener('DOMContentLoaded', () => typewriterEffect('.typed-text')) +bootDevcmAlpine(Alpine); diff --git a/src/styles/devcm-comment-widget.css b/src/styles/devcm-comment-widget.css new file mode 100644 index 0000000..4a677b2 --- /dev/null +++ b/src/styles/devcm-comment-widget.css @@ -0,0 +1,97 @@ +:host { + color: var(--devcm-text, #586e75); + font-family: Hack, Monaco, Consolas, "Ubuntu Mono", PingHei, "PingFang SC", "Microsoft YaHei", monospace; + position: relative; +} + +.comment-widget { + color: var(--devcm-text, #586e75); +} + +.comment-widget__wrapper { + display: grid; + gap: 12px; +} + +.comment-widget__stats { + padding-bottom: 10px; + border-bottom: 1px dashed var(--devcm-line, rgba(101, 123, 131, 0.2)); + color: var(--devcm-green, #859900); + font-size: 13px; +} + +.comment-widget__list { + color: var(--devcm-text, #586e75); +} + +.form { + position: relative; + display: grid; + gap: 10px; +} + +.form__editor { + width: 100%; + min-height: 96px; + padding: 12px 14px; + border: 1px solid var(--devcm-line, rgba(101, 123, 131, 0.2)); + border-radius: 8px; + outline: none; + background: linear-gradient( + 180deg, + var(--devcm-panel, rgba(10, 12, 9, 0.94)), + var(--devcm-panel-soft, rgba(5, 7, 5, 0.92)) + ); + box-shadow: var(--devcm-shadow, inset 0 0 0 1px rgba(181, 137, 0, 0.16)); + color: var(--devcm-text, #586e75); + caret-color: var(--devcm-amber, #b58900); + font: 14px/1.7 Hack, Monaco, Consolas, "Ubuntu Mono", PingHei, "PingFang SC", "Microsoft YaHei", monospace; + resize: vertical; +} + +.form__editor::placeholder { + color: var(--devcm-dim, rgba(221, 214, 185, 0.42)); +} + +.form__editor:focus { + border-color: var(--devcm-green, rgba(133, 153, 0, 0.45)); + box-shadow: 0 0 0 1px var(--devcm-green-soft, rgba(133, 153, 0, 0.16)); +} + +.form__footer { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.form__actions { + position: relative; + display: flex; + align-items: center; + gap: 8px; +} + +.form__button--login, +.form__button--submit { + min-height: 32px; + padding: 6px 12px; + border: 1px solid var(--devcm-line, rgba(101, 123, 131, 0.2)) !important; + border-radius: 8px; + background: var(--devcm-panel-soft, rgba(238, 232, 213, 0.78)) !important; + color: var(--devcm-amber, #b58900) !important; + cursor: pointer; + font: 13px/1 Hack, Monaco, Consolas, "Ubuntu Mono", PingHei, "PingFang SC", "Microsoft YaHei", monospace; +} + +.form__button--submit { + border-color: var(--devcm-green, rgba(133, 153, 0, 0.28)) !important; + background: var(--devcm-green-soft, rgba(133, 153, 0, 0.14)) !important; + color: var(--devcm-green, #859900) !important; +} + +.form__button--login:hover, +.form__button--submit:hover { + background: var(--devcm-panel-warm, rgba(238, 232, 213, 0.62)) !important; +} diff --git a/src/styles/devcm-editor.scss b/src/styles/devcm-editor.scss new file mode 100644 index 0000000..b38346f --- /dev/null +++ b/src/styles/devcm-editor.scss @@ -0,0 +1,1012 @@ +@import "variables"; + +.devcm-editor { + min-width: 0; + min-height: 0; + padding: 24px 30px 20px; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; +} + +.devcm-terminal-window, +.devcm-editor-window, +.devcm-post-card, +.devcm-category-card, +.devcm-archive-table, +.devcm-comments { + position: relative; + overflow: hidden; + border: 1px solid rgba(255, 176, 0, 0.26); + border-radius: var(--devcm-radius); + background: linear-gradient(180deg, rgba(14, 16, 12, 0.96), rgba(9, 11, 8, 0.94)); + box-shadow: var(--devcm-shadow); +} + +html[data-color-scheme="light"] .devcm-terminal-window, +html[data-color-scheme="light"] .devcm-editor-window, +html[data-color-scheme="light"] .devcm-post-card, +html[data-color-scheme="light"] .devcm-category-card, +html[data-color-scheme="light"] .devcm-archive-table, +html[data-color-scheme="light"] .devcm-comments { + border-color: rgba(181, 137, 0, 0.28); + background: rgba(253, 246, 227, 0.9); +} + +html[data-color-scheme="light"] .devcm-terminal-window::before, +html[data-color-scheme="light"] .devcm-editor-window::before, +html[data-color-scheme="light"] .devcm-post-card::before, +html[data-color-scheme="light"] .devcm-category-card::before, +html[data-color-scheme="light"] .devcm-archive-table::before, +html[data-color-scheme="light"] .devcm-comments::before { + opacity: 0; +} + +.devcm-terminal-window::before, +.devcm-editor-window::before, +.devcm-post-card::before, +.devcm-category-card::before, +.devcm-archive-table::before, +.devcm-comments::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(90deg, transparent, rgba(255, 176, 0, 0.035), transparent); + opacity: 0.34; + pointer-events: none; +} + +.devcm-window-titlebar, +.devcm-editor-tabs { + display: flex; + min-height: 38px; + align-items: center; + gap: 8px; + border-bottom: 1px solid rgba(255, 176, 0, 0.18); + background: rgba(255, 176, 0, 0.035); +} + +html[data-color-scheme="light"] .devcm-window-titlebar, +html[data-color-scheme="light"] .devcm-editor-tabs, +html[data-color-scheme="light"] .devcm-comment-titlebar { + border-bottom-color: rgba(101, 123, 131, 0.18); + background: rgba(238, 232, 213, 0.78); +} + +.devcm-window-titlebar { + padding: 0 15px; +} + +.devcm-window-dot { + width: 11px; + height: 11px; + border: 1px solid rgba(255, 176, 0, 0.72); + border-radius: 50%; +} + +.devcm-window-dot:nth-child(1) { + background: #db8b16; +} + +.devcm-window-dot:nth-child(2) { + background: #ffbd2e; +} + +.devcm-window-dot:nth-child(3) { + background: rgba(104, 243, 127, 0.15); +} + +.devcm-home-hero, +.devcm-archive-hero { + margin-bottom: 22px; +} + +.devcm-hero { + display: grid; + min-height: 190px; + grid-template-columns: minmax(0, 1fr) 160px; + gap: 24px; + align-items: center; + padding: 22px 24px 24px; +} + +.devcm-prompt-line { + margin-bottom: 14px; + color: var(--devcm-green); + text-shadow: 0 0 12px rgba(104, 243, 127, 0.18); +} + +.devcm-prompt-line span:first-child { + color: var(--devcm-green); +} + +.devcm-prompt-line span:not(:first-child) { + color: #e0b55c; +} + +.devcm-prompt-line--pending { + display: none; +} + +html[data-color-scheme="light"] .devcm-prompt-line span:first-child { + color: var(--devcm-green); +} + +html[data-color-scheme="light"] .devcm-prompt-line span:not(:first-child) { + color: var(--devcm-amber); +} + +.devcm-hero__title, +.devcm-page-title, +.devcm-article-title { + margin: 0 0 12px; + color: var(--devcm-amber); + font-weight: 900; + letter-spacing: 0; + text-shadow: 0 0 24px rgba(255, 176, 0, 0.26); +} + +.devcm-hero__title { + font-size: clamp(26px, 2.4vw, 40px); + line-height: 1.15; +} + +.devcm-hero__subtitle { + margin-bottom: 16px; + color: #d7c18d; + font-size: 16px; + word-spacing: 12px; +} + +html[data-color-scheme="light"] .devcm-hero__subtitle { + color: var(--devcm-muted); +} + +html[data-color-scheme="light"] .devcm-hero__title, +html[data-color-scheme="light"] .devcm-page-title, +html[data-color-scheme="light"] .devcm-article-title { + text-shadow: none; +} + +.devcm-hero__desc { + padding-top: 13px; + border-top: 1px solid rgba(104, 243, 127, 0.24); + color: var(--devcm-text); + line-height: 1.85; +} + +.devcm-hero__desc > :first-child { + margin-top: 0; +} + +.devcm-hero__desc > :last-child { + margin-bottom: 0; +} + +.devcm-cursor { + display: inline-block; + width: 9px; + height: 16px; + background: var(--devcm-amber); + box-shadow: 0 0 14px rgba(255, 176, 0, 0.48); + vertical-align: -2px; + animation: devcm-blink 1s steps(1) infinite; +} + +@keyframes devcm-blink { + 50% { + opacity: 0.08; + } +} + +.devcm-ascii-badge { + position: relative; + display: grid; + width: min(160px, 100%); + height: fit-content; + min-height: 0; + align-self: center; + justify-self: center; + place-items: center; + padding: 12px 10px; + overflow: hidden; + border: 1px dashed rgba(255, 176, 0, 0.46); + background: radial-gradient(circle at 50% 32%, rgba(255, 176, 0, 0.14), transparent 42%), + linear-gradient(180deg, rgba(22, 18, 10, 0.58), rgba(6, 8, 6, 0.68)); + box-shadow: inset 0 0 32px rgba(255, 176, 0, 0.07), 0 0 18px rgba(255, 176, 0, 0.04); + color: rgba(255, 176, 0, 0.82); + text-align: center; +} + +.devcm-ascii-badge__grid { + position: absolute; + inset: 8px; + border: 1px dotted rgba(255, 176, 0, 0.18); + background: repeating-linear-gradient( + 0deg, + rgba(255, 176, 0, 0.03) 0, + rgba(255, 176, 0, 0.03) 1px, + transparent 1px, + transparent 10px + ), + repeating-linear-gradient( + 90deg, + rgba(104, 243, 127, 0.025) 0, + rgba(104, 243, 127, 0.025) 1px, + transparent 1px, + transparent 10px + ); +} + +.devcm-ascii-badge__inner { + position: relative; + z-index: 1; + display: grid; + width: 100%; + justify-items: center; + gap: 8px; +} + +.devcm-ascii-badge__label { + justify-self: start; + padding-left: 6px; + color: var(--devcm-green); + font-size: 10px; + line-height: 1; + opacity: 0.72; +} + +.devcm-ascii-badge pre { + margin: 0; + border: 0; + color: var(--devcm-amber); + font: 900 clamp(34px, 3.1vw, 46px) / 0.78 var(--devcm-mono); + letter-spacing: 0; + text-shadow: 0 0 18px rgba(255, 176, 0, 0.3); + transform: scaleX(1.04); +} + +.devcm-ascii-badge small { + display: block; + color: var(--devcm-amber); + opacity: 0.8; +} + +.devcm-ascii-badge__scan { + width: min(96px, 82%); + height: 4px; + background: repeating-linear-gradient( + 90deg, + rgba(255, 176, 0, 0.75) 0, + rgba(255, 176, 0, 0.75) 7px, + transparent 7px, + transparent 12px + ); + opacity: 0.5; +} + +html[data-color-scheme="light"] .devcm-ascii-badge { + border-color: rgba(181, 137, 0, 0.42); + background: rgba(238, 232, 213, 0.72); + box-shadow: none; + color: var(--devcm-amber); +} + +html[data-color-scheme="light"] .devcm-ascii-badge pre { + text-shadow: none; +} + +.devcm-editor-window { + margin-top: 22px; +} + +.devcm-shell--home .devcm-editor-window { + min-height: clamp(360px, 43vh, 520px); +} + +.devcm-editor-tabs { + justify-content: space-between; + padding: 0 16px; + overflow-x: auto; +} + +.devcm-editor-tabs__left { + display: flex; + min-height: 38px; + align-items: stretch; +} + +.devcm-editor-tab { + display: flex; + min-width: 132px; + min-height: 38px; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 0 16px; + border-right: 1px solid rgba(255, 176, 0, 0.14); + background: rgba(255, 176, 0, 0.025); + color: #d79d31; + line-height: 1; +} + +.devcm-editor-tab > span { + display: inline-flex; + min-height: 18px; + align-items: center; + line-height: 1; +} + +.devcm-editor-tab--active { + background: rgba(255, 176, 0, 0.08); + box-shadow: inset 0 2px 0 var(--devcm-amber); + color: var(--devcm-amber); +} + +.devcm-editor-tab__close { + display: inline-grid; + width: 18px; + height: 18px; + align-self: center; + flex: 0 0 18px; + place-items: center; + padding: 0; + background: transparent; + color: currentColor; + cursor: pointer; + font: 700 13px/1 var(--devcm-mono); + line-height: 1; + opacity: 0.48; + text-decoration: none; +} + +.devcm-editor-tab__close:hover { + box-shadow: none; + opacity: 0.74; +} + +.devcm-editor-tab__add { + display: grid; + flex: 0 0 38px; + width: 38px; + min-width: 38px; + min-height: 38px; + place-items: center; + padding: 0; + border: 0; + background: transparent; + color: var(--devcm-amber); + cursor: pointer; + opacity: 0.7; + text-decoration: none; +} + +.devcm-editor-tab__add:hover { + background: rgba(255, 176, 0, 0.08); + box-shadow: inset 0 -2px 0 rgba(255, 176, 0, 0.76); + opacity: 1; +} + +.devcm-editor-body { + position: relative; + padding: 20px; +} + +.devcm-post-list-body { + padding: 14px 18px 18px; + background: repeating-linear-gradient(180deg, transparent 0, transparent 27px, rgba(255, 176, 0, 0.035) 28px); +} + +.devcm-line-layout { + position: relative; + display: grid; + grid-template-columns: 44px minmax(0, 1fr); + gap: 14px; +} + +.devcm-line-layout::before { + content: ""; + position: absolute; + inset: 0; + background: repeating-linear-gradient(180deg, transparent 0, transparent 27px, rgba(255, 176, 0, 0.045) 28px); + opacity: 0.45; + pointer-events: none; +} + +.devcm-line-layout > * { + position: relative; + z-index: 1; +} + +.devcm-shell--home .devcm-line-layout { + min-height: clamp(250px, 31vh, 390px); + align-content: start; +} + +.devcm-gutter { + display: grid; + gap: 14px; + padding-top: 6px; + color: rgba(255, 176, 0, 0.36); + text-align: right; + user-select: none; +} + +.devcm-post-stack, +.devcm-moments { + display: grid; + gap: 14px; +} + +.devcm-post-list-body .devcm-post-stack { + gap: 11px; +} + +.devcm-shell--home .devcm-post-stack { + min-height: 100%; +} + +.devcm-post-card { + padding: 18px 20px; + border-color: rgba(255, 176, 0, 0.2); + border-radius: 8px; + background: linear-gradient(135deg, rgba(20, 25, 15, 0.9), rgba(7, 8, 6, 0.84)); + transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease; +} + +.devcm-post-list-body .devcm-post-card { + padding: 14px 16px; +} + +html[data-color-scheme="light"] .devcm-post-card { + background: rgba(253, 246, 227, 0.86); +} + +html[data-color-scheme="light"] .devcm-post-card:hover { + border-color: rgba(181, 137, 0, 0.42); + box-shadow: none; +} + +.devcm-post-card:hover { + border-color: rgba(255, 176, 0, 0.46); + box-shadow: 0 0 26px rgba(255, 176, 0, 0.08); + transform: translateY(-2px); +} + +.devcm-post-card--featured::after { + content: ""; + position: absolute; + inset: 0 auto 0 0; + width: 5px; + background: linear-gradient(var(--devcm-green), rgba(104, 243, 127, 0.3)); + box-shadow: var(--devcm-green-glow); +} + +.devcm-post-card__title { + display: inline-flex; + align-items: center; + gap: 10px; + margin: 0 0 10px; + color: var(--devcm-amber); + font-size: 21px; + letter-spacing: 0; +} + +.devcm-post-list-body .devcm-post-card__title { + margin-bottom: 8px; +} + +.devcm-post-card__title a { + color: inherit; + text-decoration: none; +} + +.devcm-badge { + padding: 3px 7px; + border: 1px solid rgba(104, 243, 127, 0.32); + border-radius: 6px; + background: var(--devcm-green-soft); + color: var(--devcm-green); + font-size: 11px; + text-transform: uppercase; +} + +.devcm-post-card__meta, +.devcm-article-meta { + display: flex; + flex-wrap: wrap; + gap: 13px; + margin-bottom: 10px; + color: #a9915a; + font-size: 13px; +} + +html[data-color-scheme="light"] .devcm-post-card__meta, +html[data-color-scheme="light"] .devcm-article-meta { + color: var(--devcm-muted); +} + +.devcm-post-card__tag, +.devcm-article-meta a { + color: var(--devcm-green); + font-weight: 500; + text-decoration: none; +} + +.devcm-post-card__excerpt { + margin: 0 0 11px; + color: #cfc4a5; + line-height: 1.65; +} + +html[data-color-scheme="light"] .devcm-post-card__excerpt { + color: var(--devcm-text); +} + +.devcm-post-list-body .devcm-post-card__excerpt { + margin-bottom: 9px; +} + +.devcm-read-more { + display: inline-flex; + align-items: center; + gap: 8px; + color: #93f0a0; + font-weight: 800; + text-decoration: none; +} + +html[data-color-scheme="light"] .devcm-read-more { + color: var(--devcm-green); +} + +.devcm-list-footnote { + margin-top: 10px; + color: var(--devcm-dim); + font-size: 13px; + text-align: center; +} + +.devcm-shell--home .devcm-list-footnote { + align-self: end; + padding-top: 6px; + border-top: 1px dashed rgba(255, 176, 0, 0.1); +} + +.devcm-article { + margin-top: 0; +} + +.devcm-article-body { + position: relative; + margin: 0; + padding: 18px 22px 18px 66px; +} + +.devcm-article-body .post-line-gutter { + left: 0; + top: 18px; + width: 50px; + opacity: 0.62; + border-right-color: rgba(255, 176, 0, 0.13); +} + +.devcm-article-content { + color: #cfc3a1; + line-height: 1.78; +} + +html[data-color-scheme="light"] .devcm-article-content { + color: var(--devcm-text); +} + +.devcm-article-title { + font-size: 24px; + line-height: 1.38; +} + +.devcm-article-content h1, +.devcm-article-content h2, +.devcm-article-content h3 { + color: var(--devcm-green); + letter-spacing: 0; +} + +.devcm-article-content p, +.devcm-article-content li { + color: #cfc3a1; +} + +html[data-color-scheme="light"] .devcm-article-content p, +html[data-color-scheme="light"] .devcm-article-content li { + color: var(--devcm-text); +} + +html[data-color-scheme="light"] .devcm-article-content pre { + border-color: rgba(101, 123, 131, 0.24); + background: rgba(238, 232, 213, 0.55); + color: #586e75; +} + +html[data-color-scheme="light"] .devcm-article-content code { + color: #586e75; +} + +.devcm-article-content pre { + border: 1px solid rgba(255, 176, 0, 0.22); + border-radius: 9px; + background: rgba(5, 7, 5, 0.72); + color: #e5d4a4; + padding: 12px 14px; +} + +.devcm-article-content code { + color: #e5d4a4; +} + +.devcm-comments { + margin-top: 16px; + padding: 0; + overflow: visible; + background: linear-gradient(180deg, rgba(12, 14, 11, 0.92), rgba(7, 9, 6, 0.86)); +} + +html[data-color-scheme="light"] .devcm-comments { + background: rgba(253, 246, 227, 0.9); +} + +.devcm-comments h2 { + margin: 0 0 10px; + color: var(--devcm-green); +} + +.devcm-comment-titlebar { + display: flex; + min-height: 38px; + align-items: center; + gap: 8px; + padding: 0 14px; + border-bottom: 1px solid rgba(255, 176, 0, 0.18); + background: rgba(255, 176, 0, 0.045); + color: var(--devcm-amber); +} + +.devcm-comment-titlebar strong { + margin-left: 6px; + font-size: 13px; + font-weight: 700; +} + +.devcm-comment-body { + position: relative; + padding: 14px; + overflow: visible; + background: repeating-linear-gradient(180deg, transparent 0, transparent 27px, rgba(255, 176, 0, 0.035) 28px); +} + +.devcm-prev-next { + margin-top: 12px; +} + +.devcm-editor .pagination { + margin-top: 16px; +} + +.devcm-editor .pagination__buttons { + gap: 12px; +} + +.devcm-editor .button { + max-width: 48%; + border: 1px solid rgba(255, 176, 0, 0.22); + border-radius: 8px; + background: rgba(255, 176, 0, 0.035); + color: var(--devcm-amber); +} + +.devcm-editor .button:hover { + border-color: rgba(255, 176, 0, 0.45); + background: rgba(255, 176, 0, 0.08); +} + +.devcm-editor .button a { + min-width: 118px; + justify-content: center; + padding: 8px 14px; +} + +.devcm-category-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 13px; +} + +.devcm-category-card { + min-height: 142px; + padding: 16px; + border-radius: 9px; + background: rgba(15, 17, 12, 0.74); + color: #b7a77b; + text-decoration: none; +} + +html[data-color-scheme="light"] .devcm-category-card { + background: rgba(253, 246, 227, 0.86); + color: var(--devcm-text); +} + +.devcm-category-card h2 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 8px; + color: var(--devcm-amber); + font-size: 17px; +} + +.devcm-category-card strong { + display: block; + margin-bottom: 8px; + color: #dac082; + font-size: 14px; +} + +html[data-color-scheme="light"] .devcm-category-card strong { + color: var(--devcm-amber); +} + +.devcm-category-card p { + margin: 0; + color: #b7a77b; + font-size: 13px; + line-height: 1.55; +} + +html[data-color-scheme="light"] .devcm-category-card p { + color: var(--devcm-text); +} + +.devcm-archive-table { + border-radius: 10px; + background: rgba(7, 9, 6, 0.72); +} + +.devcm-archive-table__title { + padding: 14px 16px; + border-bottom: 1px solid rgba(255, 176, 0, 0.18); + color: var(--devcm-amber); + font-weight: 900; +} + +.devcm-archive-row { + display: grid; + grid-template-columns: minmax(0, 1.6fr) minmax(120px, 1fr) 80px; + gap: 14px; + align-items: center; + padding: 10px 16px; + border-bottom: 1px solid rgba(255, 176, 0, 0.08); + color: #c9ba90; + font-size: 13px; + text-decoration: none; +} + +html[data-color-scheme="light"] .devcm-archive-row { + border-bottom-color: rgba(101, 123, 131, 0.12); + color: var(--devcm-text); +} + +.devcm-archive-row--header { + background: rgba(255, 176, 0, 0.035); + color: #9f8a5e; +} + +html[data-color-scheme="light"] .devcm-archive-row--header { + background: rgba(238, 232, 213, 0.64); + color: var(--devcm-muted); +} + +.devcm-archive-row--month { + grid-template-columns: 1fr; + background: rgba(255, 176, 0, 0.055); + color: var(--devcm-amber); + font-weight: 800; +} + +html[data-color-scheme="light"] .devcm-archive-row--month { + background: rgba(238, 232, 213, 0.78); +} + +.devcm-archive-row b { + color: var(--devcm-green); + font-weight: 600; +} + +.devcm-moment-media { + display: grid; + width: min(100%, 620px); + gap: 8px; + margin-top: 14px; +} + +.devcm-moment-media--one { + grid-template-columns: 1fr; +} + +.devcm-moment-media--two { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.devcm-moment-media--three { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.devcm-moment-media img, +.devcm-moment-media video { + width: 100%; + border: 1px solid rgba(255, 176, 0, 0.18); + border-radius: 8px; + object-fit: cover; +} + +.devcm-moment-actions { + display: flex; + gap: 12px; + margin-top: 14px; +} + +.devcm-action-link { + display: inline-flex; + align-items: center; + gap: 6px; + border: 0; + background: transparent; + color: var(--devcm-muted); + cursor: pointer; +} + +.devcm-action-link:hover, +.devcm-action-link--active { + color: var(--devcm-amber); +} + +.devcm-moment-comments { + margin-top: 12px; +} + +.devcm-link-group + .devcm-link-group { + margin-top: 24px; +} + +.devcm-link-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.devcm-link-card { + display: grid; + grid-template-columns: 64px minmax(0, 1fr); + gap: 14px; + align-items: center; + padding: 12px; + border: 1px solid rgba(255, 176, 0, 0.2); + border-radius: 9px; + background: rgba(255, 176, 0, 0.04); + color: #cdbd92; + text-decoration: none; +} + +html[data-color-scheme="light"] .devcm-link-card { + border-color: rgba(101, 123, 131, 0.2); + background: rgba(238, 232, 213, 0.42); + color: var(--devcm-text); +} + +.devcm-link-card img { + width: 64px; + height: 64px; + border-radius: 8px; + object-fit: cover; +} + +.devcm-link-card__body { + display: grid; + gap: 4px; + min-width: 0; +} + +.devcm-link-card__body strong { + color: var(--devcm-amber); +} + +.devcm-link-card__body small { + overflow: hidden; + color: var(--devcm-green); + text-overflow: ellipsis; + white-space: nowrap; +} + +.devcm-rightbar .post-toc { + position: relative; + width: auto; + max-height: calc(100vh - 340px); + opacity: 1; +} + +.devcm-rightbar .toc-tree { + padding-left: 0; +} + +.devcm-rightbar .toc-tree::before, +.devcm-rightbar .toc-item::before { + display: none; +} + +@media (max-width: 1320px) { + .devcm-category-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 1060px) { + .devcm-hero { + grid-template-columns: 1fr; + } + + .devcm-ascii-badge { + display: none; + } + + .devcm-category-grid, + .devcm-link-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 720px) { + .devcm-editor { + padding: 14px 12px; + } + + .devcm-shell--home .devcm-editor-window, + .devcm-shell--home .devcm-line-layout { + min-height: auto; + } + + .devcm-line-layout { + grid-template-columns: 1fr; + } + + .devcm-gutter { + display: none; + } + + .devcm-hero { + min-height: auto; + padding: 18px; + } + + .devcm-hero__title { + font-size: 25px; + } + + .devcm-article-body { + padding: 14px; + } + + .devcm-article-body .post-line-gutter { + display: none; + } + + .devcm-category-grid, + .devcm-link-grid, + .devcm-moment-media--two, + .devcm-moment-media--three { + grid-template-columns: 1fr; + } + + .devcm-archive-row { + grid-template-columns: 1fr; + gap: 4px; + } +} diff --git a/src/styles/devcm-shell.scss b/src/styles/devcm-shell.scss new file mode 100644 index 0000000..56d0efc --- /dev/null +++ b/src/styles/devcm-shell.scss @@ -0,0 +1,932 @@ +@import "variables"; + +html { + height: 100%; + background: var(--devcm-bg-deep); +} + +body.devcm-body { + height: 100vh; + min-height: 0; + background: radial-gradient(circle at 46% 0%, rgba(104, 243, 127, 0.11), transparent 28%), + radial-gradient(circle at 85% 18%, rgba(255, 176, 0, 0.1), transparent 28%), + linear-gradient(180deg, #090b09 0%, #050706 100%); + color: var(--devcm-text); + font-family: var(--devcm-mono); + overflow: hidden; +} + +body.devcm-body.pixel_style { + --devcm-mono: Pixel, Monaco, Consolas, "Ubuntu Mono", PingHei, "PingFang SC", "Microsoft YaHei", monospace; + font-family: var(--devcm-mono); +} + +html[data-color-scheme="light"] body.devcm-body { + background: #eee8d5; +} + +html[data-color-scheme="light"] body.devcm-body::before { + opacity: 0.34; + background: repeating-linear-gradient( + 0deg, + rgba(147, 161, 161, 0.08) 0, + rgba(147, 161, 161, 0.08) 1px, + transparent 1px, + transparent 28px + ), + repeating-linear-gradient( + 90deg, + rgba(147, 161, 161, 0.08) 0, + rgba(147, 161, 161, 0.08) 1px, + transparent 1px, + transparent 28px + ); + mix-blend-mode: normal; +} + +html[data-color-scheme="light"] body.devcm-body::after { + opacity: 0; +} + +body.devcm-body::before, +body.devcm-body::after { + content: ""; + position: fixed; + inset: 0; + z-index: 50; + pointer-events: none; +} + +body.devcm-body::before { + opacity: 0.16; + background: repeating-linear-gradient( + 0deg, + rgba(255, 255, 255, 0.035) 0, + rgba(255, 255, 255, 0.035) 1px, + transparent 1px, + transparent 3px + ), + repeating-linear-gradient( + 90deg, + rgba(255, 176, 0, 0.025) 0, + rgba(255, 176, 0, 0.025) 1px, + transparent 1px, + transparent 28px + ); + mix-blend-mode: screen; +} + +body.devcm-body::after { + background: radial-gradient(ellipse at center, transparent 48%, rgba(0, 0, 0, 0.52) 100%); + opacity: 0.62; +} + +.devcm-shell { + position: relative; + z-index: 2; + width: min(1720px, 100vw); + height: 100vh; + min-height: 0; + margin: 0 auto; + overflow: hidden; + border-right: 1px solid rgba(255, 176, 0, 0.14); + border-left: 1px solid rgba(255, 176, 0, 0.14); + box-shadow: 0 0 54px rgba(0, 0, 0, 0.38); +} + +html[data-color-scheme="light"] .devcm-shell { + border-color: rgba(101, 123, 131, 0.18); + box-shadow: none; +} + +.devcm-app { + display: grid; + height: 100vh; + min-height: 0; + grid-template-rows: auto minmax(0, 1fr) auto; + overflow: hidden; + background: linear-gradient(90deg, rgba(255, 176, 0, 0.04) 1px, transparent 1px) 0 0 / 28px 28px, + linear-gradient(180deg, rgba(255, 176, 0, 0.032) 1px, transparent 1px) 0 0 / 28px 28px, + linear-gradient(180deg, rgba(8, 10, 8, 0.98), rgba(6, 8, 6, 0.98)); +} + +html[data-color-scheme="light"] .devcm-app { + background: linear-gradient(90deg, rgba(147, 161, 161, 0.12) 1px, transparent 1px) 0 0 / 28px 28px, + linear-gradient(180deg, rgba(147, 161, 161, 0.1) 1px, transparent 1px) 0 0 / 28px 28px, #fdf6e3; +} + +.devcm-topbar { + position: relative; + z-index: 16; + min-height: var(--devcm-topbar-height); + padding: 18px 28px 0; + border-bottom: 1px solid rgba(255, 176, 0, 0.22); + background: linear-gradient(180deg, rgba(13, 13, 10, 0.96), rgba(8, 10, 8, 0.94)), + radial-gradient(circle at 54% 12%, rgba(104, 243, 127, 0.12), transparent 28%); + box-shadow: 0 12px 34px rgba(0, 0, 0, 0.2); +} + +html[data-color-scheme="light"] .devcm-topbar { + border-bottom-color: rgba(101, 123, 131, 0.22); + background: #eee8d5; + box-shadow: none; +} + +.devcm-topbar__main { + display: grid; + grid-template-columns: max-content minmax(160px, 1fr) max-content; + gap: 18px; + align-items: center; +} + +.devcm-brand { + display: flex; + min-width: 0; + align-items: center; + gap: 12px; + color: var(--devcm-amber); + text-decoration: none; +} + +.devcm-brand__image, +.devcm-brand__mark { + width: 34px; + height: 34px; + flex: 0 0 auto; + border: 1px solid var(--devcm-line-strong); + border-radius: 8px; + background: rgba(255, 176, 0, 0.05); + box-shadow: var(--devcm-shadow); +} + +.devcm-brand__image { + object-fit: contain; + padding: 3px; +} + +.devcm-brand__mark { + display: grid; + place-items: center; + color: var(--devcm-amber); + font-size: 10px; + font-weight: 900; + line-height: 0.83; + text-align: center; + text-shadow: 0 0 12px rgba(255, 176, 0, 0.5); +} + +.devcm-brand__name { + overflow: hidden; + color: var(--devcm-amber); + font-size: 29px; + font-weight: 900; + letter-spacing: 0; + text-overflow: ellipsis; + text-shadow: 0 0 18px rgba(255, 176, 0, 0.36); + white-space: nowrap; +} + +.devcm-prompt-stream { + position: relative; + z-index: 1; + display: grid; + min-height: 34px; + min-width: 0; + grid-template-columns: max-content minmax(0, 1fr); + gap: 14px; + align-items: center; + overflow: visible; + isolation: isolate; +} + +.devcm-prompt-stream__name { + color: var(--devcm-green); + text-shadow: 0 0 12px rgba(104, 243, 127, 0.24); + white-space: nowrap; +} + +.devcm-prompt-stream__line { + min-width: 0; + opacity: 0.78; + filter: drop-shadow(0 0 7px rgba(104, 243, 127, 0.18)); +} + +.devcm-topbar__actions { + position: relative; + z-index: 2; + display: flex; + align-items: center; + gap: 10px; +} + +.devcm-icon-button { + display: grid; + flex: 0 0 34px; + width: 34px; + min-width: 34px; + height: 34px; + place-items: center; + padding: 0; + border: 0; + border-radius: 8px; + background: transparent; + color: var(--devcm-amber); + cursor: pointer; + font-size: 22px; + line-height: 1; + text-shadow: 0 0 14px rgba(255, 176, 0, 0.34); +} + +.devcm-icon-button:hover { + background: rgba(255, 176, 0, 0.08); + box-shadow: inset 0 0 0 1px rgba(255, 176, 0, 0.24); +} + +.devcm-workspace { + display: grid; + height: 100%; + min-height: 0; + grid-template-columns: var(--devcm-activity-width) var(--devcm-sidebar-width) minmax(640px, 1fr) var( + --devcm-rightbar-width + ); + overflow: hidden; +} + +.devcm-activity { + display: flex; + min-height: 0; + flex-direction: column; + align-items: center; + gap: 0; + padding: 20px 0 16px; + border-right: 1px solid rgba(255, 176, 0, 0.18); + background: rgba(5, 7, 5, 0.78); +} + +.devcm-activity__group { + display: flex; + width: 100%; + min-height: 0; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.devcm-activity__group--down { + margin-top: auto; +} + +.devcm-activity__icon-bank { + display: none; +} + +.devcm-activity__button { + position: relative; + display: grid; + width: 38px; + height: 38px; + place-items: center; + border: 1px solid transparent; + border-radius: 10px; + color: rgba(255, 221, 142, 0.84); + font-size: 21px; + text-decoration: none; +} + +.devcm-activity__button::after { + content: attr(data-label); + position: absolute; + top: 50%; + left: calc(100% + 10px); + z-index: 40; + padding: 5px 8px; + border: 1px solid rgba(255, 176, 0, 0.28); + border-radius: 6px; + background: rgba(8, 10, 7, 0.96); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.34); + color: var(--devcm-amber); + font-size: 12px; + line-height: 1; + opacity: 0; + pointer-events: none; + transform: translate(-4px, -50%); + transition: opacity 0.15s ease, transform 0.15s ease; + white-space: nowrap; +} + +.devcm-activity__button:hover::after { + opacity: 1; + transform: translate(0, -50%); +} + +.devcm-activity__button:hover, +.devcm-activity__button--active { + border-color: rgba(255, 176, 0, 0.35); + background: rgba(255, 176, 0, 0.07); + box-shadow: 0 0 16px rgba(255, 176, 0, 0.08); + color: var(--devcm-amber); +} + +.devcm-activity__button--active::before { + content: ""; + position: absolute; + top: 8px; + bottom: 8px; + left: -10px; + width: 3px; + border-radius: 999px; + background: var(--devcm-amber); + box-shadow: 0 0 16px rgba(255, 176, 0, 0.44); +} + +.devcm-activity__spacer { + flex: 1; +} + +.devcm-sidebar, +.devcm-rightbar { + min-height: 0; + overflow-x: hidden; + overflow-y: auto; + background: rgba(8, 10, 7, 0.78); + overscroll-behavior: contain; +} + +html[data-color-scheme="light"] .devcm-sidebar, +html[data-color-scheme="light"] .devcm-rightbar { + background: rgba(238, 232, 213, 0.72); +} + +html[data-color-scheme="light"] .devcm-activity { + border-right-color: rgba(101, 123, 131, 0.22); + background: #eee8d5; +} + +html[data-color-scheme="light"] .devcm-activity__button { + color: #657b83; +} + +html[data-color-scheme="light"] .devcm-activity__button:hover, +html[data-color-scheme="light"] .devcm-activity__button--active { + border-color: rgba(181, 137, 0, 0.34); + background: rgba(181, 137, 0, 0.09); + box-shadow: none; + color: var(--devcm-amber); +} + +.devcm-sidebar { + padding: 18px 0 20px; + border-right: 1px solid rgba(255, 176, 0, 0.18); +} + +.devcm-rightbar { + padding: 24px 16px 20px; + border-left: 1px solid rgba(255, 176, 0, 0.18); + background: linear-gradient(180deg, rgba(8, 10, 7, 0.84), rgba(5, 7, 5, 0.76)); +} + +.devcm-rightbar--hidden { + display: none; +} + +.devcm-shell--page .devcm-workspace, +.devcm-shell--links .devcm-workspace, +.devcm-shell--moments .devcm-workspace { + grid-template-columns: var(--devcm-activity-width) var(--devcm-sidebar-width) minmax(640px, 1fr); +} + +.devcm-side-section { + margin-bottom: 18px; + padding: 0 18px 18px; + border-bottom: 1px solid rgba(255, 176, 0, 0.16); +} + +.devcm-side-section:last-child { + border-bottom: 0; +} + +.devcm-section-title, +.devcm-widget__title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 14px; + color: var(--devcm-amber); + font-size: 14px; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.devcm-tree, +.devcm-tree__nested { + display: grid; + gap: 6px; + margin: 0; + padding: 0; + color: var(--devcm-muted); + font-size: 14px; + list-style: none; +} + +.devcm-tree__nested { + margin-left: 16px; + padding-left: 9px; + border-left: 1px dotted rgba(255, 176, 0, 0.14); +} + +.devcm-tree-folder { + display: grid; + gap: 5px; +} + +.devcm-tree-folder > summary { + cursor: pointer; + list-style: none; +} + +.devcm-tree-folder > summary::-webkit-details-marker { + display: none; +} + +.devcm-tree-folder > summary:focus { + outline: none; +} + +.devcm-tree-folder > summary:focus-visible { + box-shadow: inset 0 0 0 1px rgba(104, 243, 127, 0.36), 0 0 0 1px rgba(255, 176, 0, 0.22); +} + +.devcm-tree-folder[open] > summary > .devcm-tree__chev { + transform: rotate(90deg); +} + +.devcm-tree__item { + display: grid; + min-height: 24px; + grid-template-columns: 16px 18px minmax(0, 1fr) max-content; + gap: 6px; + align-items: center; + padding: 2px 4px; + border-radius: 6px; + color: inherit; + text-decoration: none; +} + +.devcm-tree__icon { + color: #cda44a; + font-size: 15px; + opacity: 0.82; +} + +html[data-color-scheme="light"] .devcm-tree__icon { + color: #657b83; +} + +.devcm-tree__open { + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--devcm-dim); + font-size: 12px; + text-decoration: none; +} + +.devcm-tree__open i { + font-size: 13px; +} + +.devcm-tree__open:hover { + color: var(--devcm-green); +} + +.devcm-tree__item--external .devcm-tree__icon, +.devcm-tree__open--external { + color: var(--devcm-cyan); + opacity: 0.92; +} + +.devcm-tree__item--external:hover .devcm-tree__icon, +.devcm-tree__open--external:hover { + color: var(--devcm-green); +} + +.devcm-tree__item:hover, +.devcm-tree__item--root { + background: rgba(255, 176, 0, 0.07); + color: #f4d89c; +} + +html[data-color-scheme="light"] .devcm-tree__item:hover, +html[data-color-scheme="light"] .devcm-tree__item--root { + background: rgba(181, 137, 0, 0.08); + color: #586e75; +} + +.devcm-tree__item--root { + background: transparent; + color: #e5c36c; + font-weight: 800; +} + +html[data-color-scheme="light"] .devcm-tree__item--root { + background: transparent; + color: #657b83; +} + +.devcm-tree__chev { + position: relative; + display: inline-grid; + width: 16px; + height: 16px; + place-items: center; + color: var(--devcm-amber); + opacity: 0.8; + transition: transform 0.16s ease; +} + +.devcm-tree__chev::before { + content: ""; + width: 0; + height: 0; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 5px solid currentColor; +} + +.devcm-tree__item:not(.devcm-tree__item--folder) .devcm-tree__chev::before { + width: 3px; + height: 3px; + border: 0; + border-radius: 50%; + background: currentColor; +} + +.devcm-tree__count { + color: #8a7c56; +} + +.devcm-tag-cloud { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.devcm-tag { + display: inline-flex; + min-height: 28px; + align-items: center; + padding: 4px 9px; + border: 1px solid rgba(255, 176, 0, 0.16); + border-radius: 7px; + background: rgba(255, 176, 0, 0.05); + color: #cdb37a; + font-size: 13px; + text-decoration: none; +} + +html[data-color-scheme="light"] .devcm-tag { + border-color: rgba(101, 123, 131, 0.22); + background: rgba(238, 232, 213, 0.62); + color: #657b83; +} + +.devcm-tag:hover { + border-color: rgba(255, 176, 0, 0.36); + color: var(--devcm-amber); +} + +.devcm-widget { + position: relative; + margin-bottom: 16px; + padding: 14px; + overflow: hidden; + border: 1px solid rgba(255, 176, 0, 0.26); + border-radius: 8px; + background: linear-gradient(180deg, rgba(14, 16, 12, 0.96), rgba(9, 11, 8, 0.94)); + box-shadow: var(--devcm-shadow); +} + +html[data-color-scheme="light"] .devcm-widget { + background: rgba(253, 246, 227, 0.88); +} + +.devcm-widget::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(255, 176, 0, 0.04), transparent 38%, rgba(104, 243, 127, 0.025)); + opacity: 0.45; + pointer-events: none; +} + +.devcm-widget--empty { + min-height: 112px; +} + +.devcm-widget__body { + min-height: 50px; +} + +.devcm-widget > * { + position: relative; + z-index: 1; +} + +.devcm-panel-list { + display: grid; + gap: 9px; +} + +.devcm-panel-link { + display: grid; + gap: 3px; + padding: 9px 0; + border-bottom: 1px dashed rgba(255, 176, 0, 0.12); + color: #cdb78b; + font-size: 13px; + text-decoration: none; +} + +.devcm-panel-link:last-child { + border-bottom: 0; +} + +.devcm-panel-link:hover { + color: var(--devcm-amber); +} + +.devcm-panel-link strong { + overflow: hidden; + color: inherit; + font-weight: 700; + text-overflow: ellipsis; + white-space: nowrap; +} + +.devcm-panel-link span { + color: #8c7950; +} + +html[data-color-scheme="light"] .devcm-panel-link { + border-bottom-color: rgba(101, 123, 131, 0.16); + color: #586e75; +} + +html[data-color-scheme="light"] .devcm-panel-link span { + color: #93a1a1; +} + +.devcm-info-list { + display: grid; + gap: 10px; +} + +.devcm-info-list__row { + display: grid; + grid-template-columns: 72px minmax(0, 1fr); + gap: 10px; + color: #c9bb91; + font-size: 13px; +} + +html[data-color-scheme="light"] .devcm-info-list__row { + color: #657b83; +} + +.devcm-info-list__row strong { + overflow: hidden; + color: var(--devcm-green); + font-weight: 600; + text-overflow: ellipsis; + white-space: nowrap; +} + +.devcm-statusbar { + position: relative; + z-index: 18; + display: grid; + box-sizing: border-box; + height: var(--devcm-status-height); + min-height: 0; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + gap: 18px; + align-items: center; + padding: 0 28px; + border-top: 1px solid rgba(255, 176, 0, 0.2); + background: rgba(5, 7, 5, 0.92); + color: #b99f67; + font-size: 13px; + line-height: 1; + backdrop-filter: blur(10px); +} + +html[data-color-scheme="light"] .devcm-statusbar { + border-top-color: rgba(101, 123, 131, 0.22); + background: #eee8d5; + color: #657b83; +} + +.devcm-statusbar__left, +.devcm-statusbar__right, +.devcm-statusbar__center, +.devcm-statusbar__beian { + display: flex; + height: 100%; + min-width: 0; + align-items: center; + gap: 12px; + line-height: 1; + white-space: nowrap; +} + +.devcm-statusbar span, +.devcm-statusbar a { + display: inline-flex; + align-items: center; + line-height: 1; +} + +.devcm-statusbar__right { + justify-content: flex-end; +} + +.devcm-statusbar__center { + justify-content: center; +} + +.devcm-statusbar__beian { + display: inline-flex; + height: auto; + justify-content: center; + gap: 8px; + padding: 0; + font-size: inherit; +} + +.devcm-statusbar__beian:empty { + display: none; +} + +.devcm-statusbar__beian img { + display: inline-block; + height: 1.2em; +} + +.devcm-statusbar a { + color: var(--devcm-amber); + text-decoration: none; +} + +.devcm-statusbar__dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--devcm-green); + box-shadow: 0 0 10px rgba(104, 243, 127, 0.6); +} + +@media (max-width: 1320px) { + :root { + --devcm-sidebar-width: 250px; + --devcm-rightbar-width: 270px; + } + + .devcm-workspace { + grid-template-columns: var(--devcm-activity-width) var(--devcm-sidebar-width) minmax(520px, 1fr) var( + --devcm-rightbar-width + ); + } + + .devcm-shell--page .devcm-workspace, + .devcm-shell--links .devcm-workspace, + .devcm-shell--moments .devcm-workspace { + grid-template-columns: var(--devcm-activity-width) var(--devcm-sidebar-width) minmax(520px, 1fr); + } +} + +@media (max-width: 1060px) { + .devcm-workspace { + align-content: stretch; + grid-auto-rows: auto; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr); + } + + .devcm-workspace > .devcm-sidebar, + .devcm-rightbar { + display: none; + } + + .devcm-activity { + display: flex; + flex-direction: row; + grid-column: 1 / -1; + align-items: center; + justify-content: flex-start; + gap: 10px; + padding: 8px 12px; + overflow-x: auto; + border-right: 0; + border-bottom: 1px solid rgba(255, 176, 0, 0.18); + } + + .devcm-activity__group { + width: auto; + flex: 0 0 auto; + flex-direction: row; + gap: 10px; + } + + .devcm-activity__group--down { + margin-top: 0; + margin-left: auto; + } + + .devcm-activity__button { + flex: 0 0 auto; + } + + .devcm-activity__button::after { + top: calc(100% + 8px); + left: 50%; + transform: translate(-50%, -4px); + } + + .devcm-activity__button:hover::after { + transform: translate(-50%, 0); + } + + .devcm-activity__button--active::before { + top: auto; + right: 8px; + bottom: 0; + left: 8px; + width: auto; + height: 3px; + } + + .devcm-activity__spacer { + display: none; + } +} + +@media (max-width: 720px) { + .devcm-topbar { + min-height: auto; + padding: 14px 12px 0; + } + + .devcm-topbar__main { + grid-template-columns: max-content minmax(0, 1fr) max-content; + gap: 10px; + } + + .devcm-prompt-stream { + min-height: 30px; + grid-template-columns: max-content minmax(24px, 1fr); + gap: 8px; + overflow: hidden; + } + + .devcm-prompt-stream__name { + font-size: 11px; + } + + .devcm-brand { + gap: 8px; + } + + .devcm-brand__image, + .devcm-brand__mark { + width: 30px; + height: 30px; + border-radius: 7px; + } + + .devcm-brand__name { + font-size: 20px; + } + + .devcm-workspace { + grid-template-columns: minmax(0, 1fr); + } + + .devcm-statusbar { + height: auto; + min-height: var(--devcm-status-height); + align-content: center; + grid-template-columns: 1fr; + justify-items: center; + gap: 4px; + padding: 7px; + } + + .devcm-statusbar__left, + .devcm-statusbar__right, + .devcm-statusbar__center { + height: auto; + justify-content: center; + flex-wrap: wrap; + } +} diff --git a/src/styles/devcm-tokens.scss b/src/styles/devcm-tokens.scss new file mode 100644 index 0000000..8fc7bbd --- /dev/null +++ b/src/styles/devcm-tokens.scss @@ -0,0 +1,80 @@ +:root, +html[data-color-scheme="dark"] { + color-scheme: dark; + --devcm-bg: #070908; + --devcm-bg-deep: #040504; + --devcm-panel: rgba(12, 14, 11, 0.94); + --devcm-panel-soft: rgba(18, 20, 14, 0.88); + --devcm-panel-warm: rgba(22, 18, 10, 0.74); + --devcm-line: rgba(255, 176, 0, 0.22); + --devcm-line-strong: rgba(255, 176, 0, 0.48); + --devcm-text: #ddd6b9; + --devcm-muted: #a79c78; + --devcm-dim: #756b4d; + --devcm-amber: #ffb000; + --devcm-amber-soft: #d88900; + --devcm-green: #68f37f; + --devcm-green-soft: rgba(104, 243, 127, 0.15); + --devcm-cyan: #00e5ff; + --devcm-purple: #b58cff; + --devcm-red: #ff5d5d; + --devcm-radius: 12px; + --devcm-activity-width: 58px; + --devcm-sidebar-width: 284px; + --devcm-rightbar-width: 308px; + --devcm-topbar-height: 88px; + --devcm-status-height: 34px; + --devcm-mono: Hack, Monaco, Consolas, "Ubuntu Mono", PingHei, "PingFang SC", "Microsoft YaHei", monospace; + --devcm-shadow: 0 0 22px rgba(255, 176, 0, 0.08), inset 0 0 0 1px rgba(255, 176, 0, 0.16); + --devcm-green-glow: 0 0 18px rgba(104, 243, 127, 0.14); + + --background: var(--devcm-bg); + --foreground: var(--devcm-text); + --selectionBackground: rgba(104, 243, 127, 0.24); + --blue: var(--devcm-cyan); + --green: var(--devcm-green); + --red: var(--devcm-red); + --yellow: var(--devcm-amber); + --brightBlue: var(--devcm-cyan); + --brightGreen: var(--devcm-green); + --brightRed: var(--devcm-red); +} + +html[data-color-scheme="light"] { + color-scheme: light; + --devcm-bg: #fdf6e3; + --devcm-bg-deep: #eee8d5; + --devcm-panel: rgba(253, 246, 227, 0.94); + --devcm-panel-soft: rgba(238, 232, 213, 0.78); + --devcm-panel-warm: rgba(238, 232, 213, 0.62); + --devcm-line: rgba(101, 123, 131, 0.2); + --devcm-line-strong: rgba(181, 137, 0, 0.42); + --devcm-text: #586e75; + --devcm-muted: #657b83; + --devcm-dim: #93a1a1; + --devcm-amber: #b58900; + --devcm-amber-soft: #cb4b16; + --devcm-green: #859900; + --devcm-green-soft: rgba(133, 153, 0, 0.14); + --devcm-cyan: #2aa198; + --devcm-purple: #6c71c4; + --devcm-red: #dc322f; + --devcm-shadow: inset 0 0 0 1px rgba(181, 137, 0, 0.16); + --devcm-green-glow: 0 0 0 1px rgba(133, 153, 0, 0.12); + + --background: var(--devcm-bg); + --foreground: var(--devcm-text); + --selectionBackground: rgba(42, 161, 152, 0.18); + --blue: var(--devcm-cyan); + --green: var(--devcm-green); + --red: var(--devcm-red); + --yellow: var(--devcm-amber); + --brightBlue: var(--devcm-cyan); + --brightGreen: var(--devcm-green); + --brightRed: var(--devcm-red); +} + +::selection { + background: rgba(104, 243, 127, 0.26); + color: #fff6d4; +} diff --git a/src/styles/footer.scss b/src/styles/footer.scss index 0482137..a44b8f6 100644 --- a/src/styles/footer.scss +++ b/src/styles/footer.scss @@ -3,7 +3,7 @@ .footer { padding: 40px 0 0 0; flex-grow: 0; - opacity: .5; + opacity: 0.5; &__inner { display: flex; @@ -79,5 +79,10 @@ gap: 4px; padding: 0 8px; } + + &__icon { + display: inline-block; + height: 1.2em; + } } } diff --git a/src/styles/header-dividing.scss b/src/styles/header-dividing.scss index ec33e7a..a8a8d92 100644 --- a/src/styles/header-dividing.scss +++ b/src/styles/header-dividing.scss @@ -1,4 +1,4 @@ -@import './variables.scss'; +@import "./variables.scss"; .dividing { flex: 1; @@ -10,48 +10,35 @@ --dash-spacing: 10px; --dash-width: 2px; - // 进入动画初始状态(仅 sticky 激活时播放) opacity: 1; transition: opacity 0.3s ease-in-out; // 虚线背景层 - 带遮罩 &::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient( - 90deg, - var(--foreground), - var(--foreground) var(--dash-width), - transparent 0, - transparent var(--dash-spacing) + 90deg, + var(--foreground), + var(--foreground) var(--dash-width), + transparent 0, + transparent var(--dash-spacing) ); background-size: var(--dash-spacing) 100%; animation: line-flow 3s linear infinite; // 遮罩层实现虚线淡入淡出效果 - mask-image: linear-gradient( - 90deg, - transparent 0%, - black 3%, - black 97%, - transparent 100% - ); - -webkit-mask-image: linear-gradient( - 90deg, - transparent 0%, - black 3%, - black 97%, - transparent 100% - ); + mask-image: linear-gradient(90deg, transparent 0%, black 3%, black 97%, transparent 100%); + -webkit-mask-image: linear-gradient(90deg, transparent 0%, black 3%, black 97%, transparent 100%); } // 扫描光效 - 模拟终端扫描线 &::after { - content: ''; + content: ""; position: absolute; left: -5%; top: 50%; @@ -59,48 +46,13 @@ width: 30%; height: 120%; filter: blur(4px); - background: linear-gradient( - 90deg, - transparent 0%, - var(--green) 80%, - transparent 100% - ); + background: linear-gradient(90deg, transparent 0%, var(--green) 80%, transparent 100%); opacity: 0.3; animation: scan-line 5s ease-in-out infinite; pointer-events: none; z-index: 10; transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); } - - &.sticky { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 5px; - z-index: 100; - --dash-spacing: 6px; - --dash-width: 2px; - background-color: color-mix(in srgb, var(--background) 90%, transparent); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - - // 进入动画:透明度由0到1 - animation: sticky-in 0.5s ease-in-out forwards; - opacity: 1; - - &::before { - top: calc(50% - 1px); - height: 2px; - } - - &::after { - opacity: 0.6; - filter: blur(3px); - // 调整扫描线高度以适应 5px 容器 - height: 200%; - width: 25%; - } - } } // 虚线流动动画 - 模拟加载进度 @@ -122,22 +74,13 @@ } 30% { width: 30%; - opacity: .3; + opacity: 0.3; } 70% { - opacity: .3; + opacity: 0.3; } 100% { left: 80%; opacity: 0; } } - -@keyframes sticky-in { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} diff --git a/src/styles/style.scss b/src/styles/style.scss index 2660f63..4ec3c68 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -2,16 +2,19 @@ @tailwind utilities; @tailwind components; -@import 'main'; -@import 'logo'; -@import 'buttons'; -@import 'header'; -@import 'header-dividing'; -@import 'post'; -@import 'post-line-num'; -@import 'post-toc'; -@import 'pagination'; -@import 'footer'; -@import 'typed-text'; +@import "main"; +@import "logo"; +@import "buttons"; +@import "header"; +@import "header-dividing"; +@import "post"; +@import "post-line-num"; +@import "post-toc"; +@import "pagination"; +@import "footer"; +@import "typed-text"; -@import 'theme'; \ No newline at end of file +@import "theme"; +@import "devcm-tokens"; +@import "devcm-shell"; +@import "devcm-editor"; diff --git a/src/styles/typed-text.scss b/src/styles/typed-text.scss index 049defa..3801677 100644 --- a/src/styles/typed-text.scss +++ b/src/styles/typed-text.scss @@ -1,10 +1,23 @@ /* 光标样式 */ .typed-text::after { - content: '|'; + content: none; +} + +.typed-text.typed-text--typing::after { + content: ""; display: inline-block; - opacity: 0; + width: 0.16em; + height: 0.78em; + margin-left: 0.08em; + background: var(--devcm-amber); + box-shadow: 0 0 8px rgba(255, 176, 0, 0.34); + opacity: 0.82; animation: blink 0.7s infinite alternate; /* 闪烁动画 */ - vertical-align: text-bottom; + vertical-align: 0.04em; +} + +.typed-text.typed-text--complete::after { + content: none; } /* 闪烁动画 */ @@ -12,4 +25,4 @@ to { opacity: 1; } -} \ No newline at end of file +} diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 7337dcb..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,24 +0,0 @@ -// 文字打字机效果 -export const typewriterEffect = (selectors: string) => { - const typedTextContainer = document.querySelector(selectors) - - if (!typedTextContainer) return - - const text = typedTextContainer.innerText - - // 清除原有的文本 - typedTextContainer.innerText = '' - - // 实现打字机效果 - let i = 0 - - const typewriter = () => { - if (i >= text.length) return - - typedTextContainer.innerText += text.charAt(i++) - - setTimeout(typewriter, Math.random() * 200 + 50) - } - - typewriter() -} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 29fe940..427ab87 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,83 @@ /** @type {import('tailwindcss').Config} */ const { Icons } = require("tailwindcss-plugin-icons"); + +const devcmActivityIcons = [ + "folder", + "home", + "search", + "archive", + "script-text", + "article", + "book-open", + "file", + "file-alt", + "file-multiple", + "note", + "notes", + "label", + "label-alt", + "user", + "users", + "contact", + "git-branch", + "git-commit", + "git-merge", + "git-pull-request", + "github", + "link", + "external-link", + "open", + "code", + "command", + "bug", + "debug", + "server", + "cloud", + "monitor", + "device-laptop", + "dashboard", + "analytics", + "chart", + "chart-bar", + "kanban", + "timeline", + "mail", + "message", + "chat", + "comment", + "notification", + "calendar", + "clock", + "bookmark", + "heart", + "lightbulb", + "shield", + "lock", + "image", + "image-gallery", + "camera", + "music", + "video", + "shopping-bag", + "cart", + "wallet", + "map", + "pin", + "flag", + "power", + "upload", + "download", + "edit", + "trash", + "plus", + "close", + "sliders", + "sliders-2", + "visible", + "eye", + "eye-closed", +]; + module.exports = { content: ["./templates/**/*.html", "./src/main.ts"], theme: { @@ -12,9 +90,14 @@ module.exports = { pixelarticons: { includeAll: true, }, + "radix-icons": { + icons: { + gear: {}, + }, + }, })), ], - darkMode: ['class', '[data-color-scheme="dark"]'], + darkMode: ["class", '[data-color-scheme="dark"]'], safelist: [ "prose-sm", "prose-base", @@ -26,5 +109,6 @@ module.exports = { "prose-zinc", "prose-neutral", "prose-stone", + ...devcmActivityIcons.map((icon) => `i-pixelarticons-${icon}`), ], -}; \ No newline at end of file +}; diff --git a/templates/archives.html b/templates/archives.html index 2ad4459..b1ff4a9 100644 --- a/templates/archives.html +++ b/templates/archives.html @@ -1,66 +1,68 @@ -
-
-

文章归档

- - - +
+
+ + +
-
+ +
+
Archive list
+
+ Title + Tags + Date +
+ + +
+ + Archive group + (0) + +
+ + 文章标题 + + #tag + + 01-01 + +
+
+
+ + diff --git a/templates/categories.html b/templates/categories.html index 5c88ca3..37e55a8 100644 --- a/templates/categories.html +++ b/templates/categories.html @@ -1,16 +1,43 @@ -
-
-

所有分类

-
    -
  • -
+
+
+ + +
-
+
+
+
+ admin@devcm:~$ + archive --view categories +
+

Categories

+

+ Browse posts by category and explore the site structure. +

+
+
+ + +
+ +

+ + Category +

+ 0 articles +

+ Category description +

+
+
diff --git a/templates/category.html b/templates/category.html index 0ac4b59..232b25c 100644 --- a/templates/category.html +++ b/templates/category.html @@ -1,68 +1,53 @@ -
-
-

- 分类:分类名 (n 篇文章) -

- 所有分类 - -
-