├── 404.php ├── README.md ├── archive.php ├── archives.php ├── comments.php ├── css ├── default-skin.min.css ├── mod.css ├── photoswipe.min.css └── style.css ├── footer.php ├── functions.php ├── head.php ├── header.php ├── index.php ├── js ├── main.js ├── photoswipe-ui-default.min.js ├── photoswipe.min.js └── vibrant.min.js ├── links.php ├── page.php ├── post.php ├── postlist.php ├── screenshot.png ├── sidebar.php └── toc.php /404.php: -------------------------------------------------------------------------------- 1 | 2 | need('head.php'); ?> 3 | 4 | need('header.php'); ?> 5 |
6 |
7 |
8 | 9 |
10 |
11 | need('footer.php'); ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | 3 | 本主题从Hugo主题`Stack`移植而来. 4 | 5 | 原项目地址 6 | 7 | https://github.com/CaiJimmy/hugo-theme-stack 8 | 9 | ## 使用 10 | 11 | - 站点 LOGO 地址 (为左侧边栏头像) 12 | 13 | - 站点 Favicon 地址 ( Favicon ) 14 | 15 | - 根据页面的 slug 添加对应的图标 16 | 17 | 图标来源: 18 | https://tabler.io/icons 19 | 20 | 匹配的slug有 21 | 22 | 关于 `about` 23 | 归档 `archives` 24 | 留言 `gbook` 25 | 评论 `messages` 26 | 链接 `links` 27 | 28 | 新建页面时请保持以上的别名,否则图标不会显示. 29 | 30 | 31 | - 是否使用魔改风格 (mod风格来自其他`Stack`用户) 32 | 33 | - 分类图片目录 (按照分类的 mid 以jpg的格式 存放的目录 ,譬如 本地目录 或者 CDN 等,用于匹配归档页面的分类图片 ) 34 | 35 | - 使用第三方评论 (可以选择使用第三方的评论系统 如 `twikoo` 等) 36 | 37 | - Header代码 (用于身份验证 等,支持HTML语法) 38 | 39 | - Footer代码 (用于插入备案号码 或者 统计代码等,支持HTML语法) 40 | 41 | - 自定义社交图标 (支持HTML语法) 42 | 43 | ```html 44 |
  • 45 | 46 | 47 | 48 |
  • 49 | ``` 50 | 保持以上格式添加 51 | 52 | ## 声明 53 | 54 | 版权归原作者所有,建议保留链接.谢谢! -------------------------------------------------------------------------------- /archive.php: -------------------------------------------------------------------------------- 1 | 2 | need('head.php'); ?> 3 | 4 | need('header.php'); ?> 5 | need('sidebar.php'); ?> 6 | need('postlist.php'); ?> 7 | need('footer.php'); ?> -------------------------------------------------------------------------------- /archives.php: -------------------------------------------------------------------------------- 1 | 8 | need('head.php'); ?> 9 | 10 | need('header.php'); ?> 11 |
    12 |
    13 |

    全部分类

    14 |
    15 |
    16 | widget('Widget_Metas_Category_List')->to($categories); ?> 17 | next()): ?> 18 | mid; $imgUrl = $this->options->imgurl; $categoryImage = $imgUrl . '/' . $categoryId . '.jpg';?> 19 | 29 | 30 |
    31 |
    32 |
    33 | publishedPostsNum)->to($archives); 36 | $year = 0; 37 | $output = '
    '; // Start archives container 38 | while ($archives->next()) { 39 | $year_tmp = date('Y', $archives->created); 40 | if ($year != $year_tmp) { 41 | if ($year > 0) { 42 | $output .= '
    '; // 结束上一个年份的月份列表和包裹的div 43 | } 44 | $year = $year_tmp; 45 | $output .= ' 46 |

    ' . $year . '

    '; // 开始新的年份div 47 | } 48 | // 输出文章项 49 | $output .= ' 61 | '; 62 | } 63 | $output .= '
    '; // End archives container 64 | echo $output; 65 | ?> 66 | 67 | need('footer.php'); ?> -------------------------------------------------------------------------------- /comments.php: -------------------------------------------------------------------------------- 1 | 2 |
    3 | comments()->to($comments); ?> 4 | allow('comment')): ?> 5 | is('attachment')) : ?> 6 | 7 | 8 |

    9 | commentsNum(_t('0'), _t('1'), _t('%d')); ?> 16 |

    17 |
      18 | have()): ?> 19 | listComments(); ?> 20 | pageNav( 22 | '', 23 | '', 24 | 1, 25 | '...', 26 | array( 27 | 'wrapTag' => 'div', 28 | 'wrapClass' => 'pagination_page', 29 | 'itemTag' => '', 30 | 'textTag' => 'a', 31 | 'currentClass' => 'active', 32 | 'prevClass' => 'prev', 33 | 'nextClass' => 'next' 34 | ) 35 | ); 36 | ?> 37 | 38 |

      39 | 40 |
      41 | 42 |
      43 | user->hasLogin()): ?> 44 |

      45 | 46 | user->screenName(); ?>. 47 | »

      48 | 49 | 50 |

      51 | 52 |

      53 |

      54 | options->commentsRequireMail): ?> required /> 55 |

      56 |

      57 | options->commentsRequireURL): ?> required /> 58 |

      59 | 60 |

      61 | 62 |

      63 | 64 |

      65 | 66 |

      67 |
      68 |
      69 | 70 | 71 | 72 | 73 | options->twikoo(); ?> 74 |
      75 | authorId) { 79 | if ($comments->authorId == $comments->ownerId) { 80 | $commentClass .= ' comment-by-author'; 81 | } else { 82 | $commentClass .= ' comment-by-user'; 83 | } 84 | } 85 | $depth = $comments->levels + 1; 86 | ?> 87 |
    1. 95 |
      96 |
      97 |
      98 | url): ?> 99 | gravatar('40', ''); ?> 100 | 101 | gravatar('40', ''); ?> 102 | 103 |
      104 |
      105 |
      author; ?> 106 |
      date('Y-m-d H:i'); ?>
      107 | 108 | reply(''); ?> 109 | 110 |
      111 |
      112 |
      113 |
      114 | content(); ?> 115 |
      116 |
      117 | children) { ?> 118 |
        119 | threadedComments($options); ?> 120 |
      121 | 122 |
    2. 123 | 124 |
        125 | comments()->to($comments); ?> 126 | next()): ?> 127 | 128 |
      129 | -------------------------------------------------------------------------------- /css/default-skin.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using clean-css v4.2.1. 3 | * Original file: /npm/photoswipe@4.1.3/dist/default-skin/default-skin.css 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | /*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */ 8 | .pswp__button{width:44px;height:44px;position:relative;background:0 0;cursor:pointer;overflow:visible;-webkit-appearance:none;display:block;border:0;padding:0;margin:0;float:right;opacity:.75;-webkit-transition:opacity .2s;transition:opacity .2s;-webkit-box-shadow:none;box-shadow:none}.pswp__button:focus,.pswp__button:hover{opacity:1}.pswp__button:active{outline:0;opacity:.9}.pswp__button::-moz-focus-inner{padding:0;border:0}.pswp__ui--over-close .pswp__button--close{opacity:1}.pswp__button,.pswp__button--arrow--left:before,.pswp__button--arrow--right:before{background:url(default-skin.png) 0 0 no-repeat;background-size:264px 88px;width:44px;height:44px}@media (-webkit-min-device-pixel-ratio:1.1),(-webkit-min-device-pixel-ratio:1.09375),(min-resolution:105dpi),(min-resolution:1.1dppx){.pswp--svg .pswp__button,.pswp--svg .pswp__button--arrow--left:before,.pswp--svg .pswp__button--arrow--right:before{background-image:url(default-skin.svg)}.pswp--svg .pswp__button--arrow--left,.pswp--svg .pswp__button--arrow--right{background:0 0}}.pswp__button--close{background-position:0 -44px}.pswp__button--share{background-position:-44px -44px}.pswp__button--fs{display:none}.pswp--supports-fs .pswp__button--fs{display:block}.pswp--fs .pswp__button--fs{background-position:-44px 0}.pswp__button--zoom{display:none;background-position:-88px 0}.pswp--zoom-allowed .pswp__button--zoom{display:block}.pswp--zoomed-in .pswp__button--zoom{background-position:-132px 0}.pswp--touch .pswp__button--arrow--left,.pswp--touch .pswp__button--arrow--right{visibility:hidden}.pswp__button--arrow--left,.pswp__button--arrow--right{background:0 0;top:50%;margin-top:-50px;width:70px;height:100px;position:absolute}.pswp__button--arrow--left{left:0}.pswp__button--arrow--right{right:0}.pswp__button--arrow--left:before,.pswp__button--arrow--right:before{content:'';top:35px;background-color:rgba(0,0,0,.3);height:30px;width:32px;position:absolute}.pswp__button--arrow--left:before{left:6px;background-position:-138px -44px}.pswp__button--arrow--right:before{right:6px;background-position:-94px -44px}.pswp__counter,.pswp__share-modal{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pswp__share-modal{display:block;background:rgba(0,0,0,.5);width:100%;height:100%;top:0;left:0;padding:10px;position:absolute;z-index:1600;opacity:0;-webkit-transition:opacity .25s ease-out;transition:opacity .25s ease-out;-webkit-backface-visibility:hidden;will-change:opacity}.pswp__share-modal--hidden{display:none}.pswp__share-tooltip{z-index:1620;position:absolute;background:#fff;top:56px;border-radius:2px;display:block;width:auto;right:44px;-webkit-box-shadow:0 2px 5px rgba(0,0,0,.25);box-shadow:0 2px 5px rgba(0,0,0,.25);-webkit-transform:translateY(6px);-ms-transform:translateY(6px);transform:translateY(6px);-webkit-transition:-webkit-transform .25s;transition:transform .25s;-webkit-backface-visibility:hidden;will-change:transform}.pswp__share-tooltip a{display:block;padding:8px 12px;color:#000;text-decoration:none;font-size:14px;line-height:18px}.pswp__share-tooltip a:hover{text-decoration:none;color:#000}.pswp__share-tooltip a:first-child{border-radius:2px 2px 0 0}.pswp__share-tooltip a:last-child{border-radius:0 0 2px 2px}.pswp__share-modal--fade-in{opacity:1}.pswp__share-modal--fade-in .pswp__share-tooltip{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}.pswp--touch .pswp__share-tooltip a{padding:16px 12px}a.pswp__share--facebook:before{content:'';display:block;width:0;height:0;position:absolute;top:-12px;right:15px;border:6px solid transparent;border-bottom-color:#fff;-webkit-pointer-events:none;-moz-pointer-events:none;pointer-events:none}a.pswp__share--facebook:hover{background:#3e5c9a;color:#fff}a.pswp__share--facebook:hover:before{border-bottom-color:#3e5c9a}a.pswp__share--twitter:hover{background:#55acee;color:#fff}a.pswp__share--pinterest:hover{background:#ccc;color:#ce272d}a.pswp__share--download:hover{background:#ddd}.pswp__counter{position:absolute;left:0;top:0;height:44px;font-size:13px;line-height:44px;color:#fff;opacity:.75;padding:0 10px}.pswp__caption{position:absolute;left:0;bottom:0;width:100%;min-height:44px}.pswp__caption small{font-size:11px;color:#bbb}.pswp__caption__center{text-align:left;max-width:420px;margin:0 auto;font-size:13px;padding:10px;line-height:20px;color:#ccc}.pswp__caption--empty{display:none}.pswp__caption--fake{visibility:hidden}.pswp__preloader{width:44px;height:44px;position:absolute;top:0;left:50%;margin-left:-22px;opacity:0;-webkit-transition:opacity .25s ease-out;transition:opacity .25s ease-out;will-change:opacity;direction:ltr}.pswp__preloader__icn{width:20px;height:20px;margin:12px}.pswp__preloader--active{opacity:1}.pswp__preloader--active .pswp__preloader__icn{background:url(preloader.gif) 0 0 no-repeat}.pswp--css_animation .pswp__preloader--active{opacity:1}.pswp--css_animation .pswp__preloader--active .pswp__preloader__icn{-webkit-animation:clockwise .5s linear infinite;animation:clockwise .5s linear infinite}.pswp--css_animation .pswp__preloader--active .pswp__preloader__donut{-webkit-animation:donut-rotate 1s cubic-bezier(.4,0,.22,1) infinite;animation:donut-rotate 1s cubic-bezier(.4,0,.22,1) infinite}.pswp--css_animation .pswp__preloader__icn{background:0 0;opacity:.75;width:14px;height:14px;position:absolute;left:15px;top:15px;margin:0}.pswp--css_animation .pswp__preloader__cut{position:relative;width:7px;height:14px;overflow:hidden}.pswp--css_animation .pswp__preloader__donut{-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;border:2px solid #fff;border-radius:50%;border-left-color:transparent;border-bottom-color:transparent;position:absolute;top:0;left:0;background:0 0;margin:0}@media screen and (max-width:1024px){.pswp__preloader{position:relative;left:auto;top:auto;margin:0;float:right}}@-webkit-keyframes clockwise{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes clockwise{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes donut-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}50%{-webkit-transform:rotate(-140deg);transform:rotate(-140deg)}100%{-webkit-transform:rotate(0);transform:rotate(0)}}@keyframes donut-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}50%{-webkit-transform:rotate(-140deg);transform:rotate(-140deg)}100%{-webkit-transform:rotate(0);transform:rotate(0)}}.pswp__ui{-webkit-font-smoothing:auto;visibility:visible;opacity:1;z-index:1550}.pswp__top-bar{position:absolute;left:0;top:0;height:44px;width:100%}.pswp--has_mouse .pswp__button--arrow--left,.pswp--has_mouse .pswp__button--arrow--right,.pswp__caption,.pswp__top-bar{-webkit-backface-visibility:hidden;will-change:opacity;-webkit-transition:opacity 333ms cubic-bezier(.4,0,.22,1);transition:opacity 333ms cubic-bezier(.4,0,.22,1)}.pswp--has_mouse .pswp__button--arrow--left,.pswp--has_mouse .pswp__button--arrow--right{visibility:visible}.pswp__caption,.pswp__top-bar{background-color:rgba(0,0,0,.5)}.pswp__ui--fit .pswp__caption,.pswp__ui--fit .pswp__top-bar{background-color:rgba(0,0,0,.3)}.pswp__ui--idle .pswp__top-bar{opacity:0}.pswp__ui--idle .pswp__button--arrow--left,.pswp__ui--idle .pswp__button--arrow--right{opacity:0}.pswp__ui--hidden .pswp__button--arrow--left,.pswp__ui--hidden .pswp__button--arrow--right,.pswp__ui--hidden .pswp__caption,.pswp__ui--hidden .pswp__top-bar{opacity:.001}.pswp__ui--one-slide .pswp__button--arrow--left,.pswp__ui--one-slide .pswp__button--arrow--right,.pswp__ui--one-slide .pswp__counter{display:none}.pswp__element--disabled{display:none!important}.pswp--minimal--dark .pswp__top-bar{background:0 0} 9 | /*# sourceMappingURL=/sm/5e51c76577f27589961cf30ca04ec05b4547dbdb911554da60e27340f7125f98.map */ -------------------------------------------------------------------------------- /css/photoswipe.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using clean-css v4.2.1. 3 | * Original file: /npm/photoswipe@4.1.3/dist/photoswipe.css 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | /*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */ 8 | .pswp{display:none;position:absolute;width:100%;height:100%;left:0;top:0;overflow:hidden;-ms-touch-action:none;touch-action:none;z-index:1500;-webkit-text-size-adjust:100%;-webkit-backface-visibility:hidden;outline:0}.pswp *{-webkit-box-sizing:border-box;box-sizing:border-box}.pswp img{max-width:none}.pswp--animate_opacity{opacity:.001;will-change:opacity;-webkit-transition:opacity 333ms cubic-bezier(.4,0,.22,1);transition:opacity 333ms cubic-bezier(.4,0,.22,1)}.pswp--open{display:block}.pswp--zoom-allowed .pswp__img{cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.pswp--zoomed-in .pswp__img{cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.pswp--dragging .pswp__img{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.pswp__bg{position:absolute;left:0;top:0;width:100%;height:100%;background:#000;opacity:0;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;will-change:opacity}.pswp__scroll-wrap{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden}.pswp__container,.pswp__zoom-wrap{-ms-touch-action:none;touch-action:none;position:absolute;left:0;right:0;top:0;bottom:0}.pswp__container,.pswp__img{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.pswp__zoom-wrap{position:absolute;width:100%;-webkit-transform-origin:left top;-ms-transform-origin:left top;transform-origin:left top;-webkit-transition:-webkit-transform 333ms cubic-bezier(.4,0,.22,1);transition:transform 333ms cubic-bezier(.4,0,.22,1)}.pswp__bg{will-change:opacity;-webkit-transition:opacity 333ms cubic-bezier(.4,0,.22,1);transition:opacity 333ms cubic-bezier(.4,0,.22,1)}.pswp--animated-in .pswp__bg,.pswp--animated-in .pswp__zoom-wrap{-webkit-transition:none;transition:none}.pswp__container,.pswp__zoom-wrap{-webkit-backface-visibility:hidden}.pswp__item{position:absolute;left:0;right:0;top:0;bottom:0;overflow:hidden}.pswp__img{position:absolute;width:auto;height:auto;top:0;left:0}.pswp__img--placeholder{-webkit-backface-visibility:hidden}.pswp__img--placeholder--blank{background:#222}.pswp--ie .pswp__img{width:100%!important;height:auto!important;left:0;top:0}.pswp__error-msg{position:absolute;left:0;top:50%;width:100%;text-align:center;font-size:14px;line-height:16px;margin-top:-8px;color:#ccc}.pswp__error-msg a{color:#ccc;text-decoration:underline} 9 | /*# sourceMappingURL=/sm/80b5c667e4998e93afdad77dccf4ffae6e6c668e71f4e3f30f4dc1952db333f5.map */ -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 | 2 |
      3 | 6 |
      7 | Powered by Typecho· 8 | Theme Stack· designed by Jimmy 9 | · Made with Sun 10 |
      11 | 页面加载耗时 💻️ author(); ?> 在线 12 |
      13 | options->tongji() ?> 14 |
      15 |
      16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | addInput($logoUrl); 6 | $icoUrl = new Typecho_Widget_Helper_Form_Element_Text('icoUrl', NULL, NULL, _t('站点 Favicon 地址')); 7 | $form->addInput($icoUrl); 8 | $instagramurl = new Typecho_Widget_Helper_Form_Element_Text('instagramurl', NULL, NULL, _t('Instagram'), _t('会在个人信息显示')); 9 | $form->addInput($instagramurl); 10 | $telegramurl = new Typecho_Widget_Helper_Form_Element_Text('telegramurl', NULL, NULL, _t('电报'), _t('会在个人信息显示')); 11 | $form->addInput($telegramurl); 12 | $githuburl = new Typecho_Widget_Helper_Form_Element_Text('githuburl', NULL, NULL, _t('github'), _t('会在个人信息显示')); 13 | $form->addInput($githuburl); 14 | $twitterurl = new Typecho_Widget_Helper_Form_Element_Text('twitterurl', NULL, NULL, _t('twitter'), _t('会在个人信息显示')); 15 | $form->addInput($twitterurl); 16 | $mastodonurl = new Typecho_Widget_Helper_Form_Element_Text('mastodonurl', NULL, NULL, _t('mastodon'), _t('会在个人信息显示')); 17 | $form->addInput($mastodonurl); 18 | $sidebarBlock = new \Typecho\Widget\Helper\Form\Element\Checkbox( 19 | 'sidebarBlock', 20 | [ 21 | 'ShowSearch' => _t('显示搜索'), 22 | 'ShowGD' => _t('显示日期归档'), 23 | 'ShowFL' => _t('显示全部分类'), 24 | 'ShowTags' => _t('显示标签'), 25 | ], 26 | ['ShowSearch', 'ShowGD', 'ShowFL', 'ShowTags'], 27 | _t('侧边栏显示') 28 | ); 29 | $form->addInput($sidebarBlock->multiMode()); 30 | $cnavatar = new Typecho_Widget_Helper_Form_Element_Text('cnavatar', NULL, NULL, _t('Gravatar镜像'), _t('默认https://cravatar.cn/avatar/,建议保持默认')); 31 | $form->addInput($cnavatar); 32 | $imgurl = new Typecho_Widget_Helper_Form_Element_Text('imgurl', NULL, NULL, _t('分类图片目录'), _t('在目录下放入对应分类mid的jpg图片')); 33 | $form->addInput($imgurl); 34 | $twikoo = new Typecho_Widget_Helper_Form_Element_Textarea('twikoo', NULL, NULL, _t('使用第三方评论'), _t('不填写则不显示')); 35 | $form->addInput($twikoo); 36 | $addhead = new Typecho_Widget_Helper_Form_Element_Textarea('addhead', NULL, NULL, _t('Header代码'), _t('在head中插入代码,支持HTML')); 37 | $form->addInput($addhead); 38 | $tongji = new Typecho_Widget_Helper_Form_Element_Textarea('tongji', NULL, NULL, _t('Footer代码'), _t('在footer中插入代码支持HTML')); 39 | $form->addInput($tongji); 40 | $addsns = new Typecho_Widget_Helper_Form_Element_Textarea('addsns', NULL, NULL, _t('自定义社交联系方式'), _t('头像下方的社交联系方式,具体使用查看使用文档')); 41 | $form->addInput($addsns); 42 | $showmod = new Typecho_Widget_Helper_Form_Element_Radio('showmod', 43 | array('0'=> _t('否'), '1'=> _t('是')), 44 | '0', _t('是否使用MOD风格'), _t('选择“是”将展示。')); 45 | $form->addInput($showmod); 46 | } 47 | 48 | // 自定义字段 49 | function themeFields($layout) { 50 | $summary= new Typecho_Widget_Helper_Form_Element_Textarea('summary', NULL, NULL, _t('文章摘要'), _t('自定义摘要')); 51 | $layout->addItem($summary); 52 | $cover= new Typecho_Widget_Helper_Form_Element_Text('cover', NULL, NULL, _t('文章封面'), _t('自定义文章封面')); 53 | $layout->addItem($cover); 54 | } 55 | 56 | // 获取Typecho的选项 57 | $options = Typecho_Widget::widget('Widget_Options'); 58 | // 检查cnavatar是否已设置,如果未设置或为空,则使用默认的Gravatar前缀 59 | $gravatarPrefix = empty($options->cnavatar) ? 'https://cravatar.cn/avatar/' : $options->cnavatar; 60 | // 定义全局常量__TYPECHO_GRAVATAR_PREFIX__,用于存储Gravatar前缀 61 | define('__TYPECHO_GRAVATAR_PREFIX__', $gravatarPrefix); 62 | 63 | //获取头图 64 | function img_postthumb($cid) { 65 | $db = Typecho_Db::get(); 66 | $rs = $db->fetchRow($db->select('table.contents.text') 67 | ->from('table.contents') 68 | ->where('table.contents.cid=?', $cid) 69 | ->order('table.contents.cid', Typecho_Db::SORT_ASC) 70 | ->limit(1)); 71 | // 检查是否获取到结果 72 | if (!$rs) { 73 | return ""; 74 | } 75 | preg_match_all("/https?:\/\/[^\s]*.(png|jpeg|jpg|gif|bmp|webp)/", $rs['text'], $thumbUrl); //通过正则式获取图片地址 76 | // 检查是否匹配到图片URL 77 | if (count($thumbUrl[0]) > 0) { 78 | return $thumbUrl[0][0]; // 返回第一张图片的URL 79 | } else { 80 | return ""; // 没有匹配到图片URL,返回空字符串 81 | } 82 | } 83 | 84 | //文章目录功能-给文章内标题加上id+超链接新窗口打开 85 | function addHeaderLinks($text) { 86 | return preg_replace_callback('/(.*?)<\/h\1>/', function ($matches) { 87 | $level = $matches[1]; 88 | $title = $matches[2]; 89 | $id = htmlspecialchars(strip_tags($title), ENT_QUOTES, 'UTF-8'); 90 | return sprintf('%s', $level, $id, $id, $title, $title, $level); 91 | }, preg_replace('/', $text)); 92 | } 93 | 94 | //文章最后修改时间 95 | function get_last_modified_time($postId) { 96 | // 获取数据库对象 97 | $db = Typecho_Db::get(); 98 | $prefix = $db->getPrefix(); 99 | 100 | // 查询文章的最后修改时间 101 | $query = $db->select('modified') 102 | ->from($prefix . 'contents') 103 | ->where('cid = ?', $postId) 104 | ->limit(1); 105 | // 执行查询 106 | $row = $db->fetchRow($query); 107 | // 检查是否有结果 108 | if ($row) { 109 | // 返回格式化后的时间 110 | return date('Y-m-d H:i:s', $row['modified']); 111 | } else { 112 | // 如果没有结果,返回空字符串 113 | return ''; 114 | } 115 | } 116 | 117 | //阅读时间 118 | function getReadingTime($text, $wordsPerMinute = 500) { 119 | // 移除HTML标签 120 | $text = strip_tags($text); 121 | // 移除多余的空格 122 | $text = trim($text); 123 | // 计算字数 124 | $wordCount = mb_strlen($text, 'UTF-8'); 125 | // 计算阅读时间 126 | $readingTime = ceil($wordCount / $wordsPerMinute); 127 | return $readingTime; 128 | } 129 | 130 | /* 131 | * 文章浏览数统计 132 | */ 133 | function get_post_view($archive) { 134 | $cid = $archive->cid; 135 | $db = Typecho_Db::get(); 136 | $prefix = $db->getPrefix(); 137 | if (!array_key_exists('views', $db->fetchRow($db->select()->from('table.contents')))) { 138 | $db->query('ALTER TABLE `' . $prefix . 'contents` ADD `views` INT(10) DEFAULT 0;'); 139 | echo 0; 140 | return; 141 | } 142 | $row = $db->fetchRow($db->select('views')->from('table.contents')->where('cid = ?', $cid)); 143 | if ($archive->is('single')) { 144 | $views = Typecho_Cookie::get('extend_contents_views'); 145 | if (empty($views)) { 146 | $views = array(); 147 | } else { 148 | $views = explode(',', $views); 149 | } 150 | if (!in_array($cid, $views)) { 151 | $db->query($db->update('table.contents')->rows(array('views' => (int)$row['views'] + 1))->where('cid = ?', $cid)); 152 | array_push($views, $cid); 153 | $views = implode(',', $views); 154 | Typecho_Cookie::set('extend_contents_views', $views); //记录查看cookie 155 | 156 | } 157 | } 158 | echo $row['views']; 159 | } 160 | 161 | /** 162 | * 页面加载时间 163 | */ 164 | function timer_start() { 165 | global $timestart; 166 | $mtime = explode( ' ', microtime() ); 167 | $timestart = $mtime[1] + $mtime[0]; 168 | return true; 169 | } 170 | timer_start(); 171 | function timer_stop( $display = 0, $precision = 3 ) { 172 | global $timestart, $timeend; 173 | $mtime = explode( ' ', microtime() ); 174 | $timeend = $mtime[1] + $mtime[0]; 175 | $timetotal = number_format( $timeend - $timestart, $precision ); 176 | $r = $timetotal < 1 ? $timetotal * 1000 . " ms" : $timetotal . " s"; 177 | if ( $display ) { 178 | echo $r; 179 | } 180 | return $r; 181 | } 182 | 183 | /*** 184 | * 在线状态 185 | */ 186 | function get_last_login($user){ 187 | $user = '1'; 188 | $now = time(); 189 | $db = Typecho_Db::get(); 190 | $prefix = $db->getPrefix(); 191 | $row = $db->fetchRow($db->select('activated')->from('table.users')->where('uid = ?', $user)); 192 | if ($row) { 193 | echo Typecho_I18n::dateWord($row['activated'], $now); 194 | } else { 195 | echo '博主一直在这里'; 196 | } 197 | } 198 | 199 | /** 200 | * Typecho后台附件增强:图片预览、批量插入、保留官方删除按钮与逻辑 201 | * @author jkjoy 202 | * @date 2025-04-25 203 | */ 204 | Typecho_Plugin::factory('admin/write-post.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures'); 205 | Typecho_Plugin::factory('admin/write-page.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures'); 206 | 207 | class AttachmentHelper { 208 | public static function addEnhancedFeatures() { 209 | ?> 210 | 235 | 351 | -------------------------------------------------------------------------------- /head.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <?php $this->archiveTitle([ 10 | 'category' => _t('分类 %s 下的文章'), 11 | 'search' => _t('包含关键字 %s 的文章'), 12 | 'tag' => _t('标签 %s 下的文章'), 13 | 'author' => _t('%s 发布的文章') 14 | ], '', ' - '); ?><?php $this->options->title(); ?> 15 | 16 | options->showmod):?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | options->addhead() ?> 31 | header("generator=&template="); ?> 32 | 33 | 41 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | 2 |
      3 | 99 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 12 | need('head.php'); ?> 13 | 14 | need('header.php'); ?> 15 | need('sidebar.php'); ?> 16 | need('postlist.php'); ?> 17 | need('footer.php'); ?> -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | var g = class e { galleryUID; items = []; constructor(t, r = 1) { if (window.PhotoSwipe == null || window.PhotoSwipeUI_Default == null) { console.error("PhotoSwipe lib not loaded."); return } this.galleryUID = r, e.createGallery(t), this.loadItems(t), this.bindClick() } loadItems(t) { this.items = []; let r = t.querySelectorAll("figure.gallery-image"); for (let i of r) { let n = i.querySelector("figcaption"), o = i.querySelector("img"), s = { w: parseInt(o.getAttribute("width")), h: parseInt(o.getAttribute("height")), src: o.src, msrc: o.getAttribute("data-thumb") || o.src, el: i }; n && (s.title = n.innerHTML), this.items.push(s) } } static createGallery(t) { let r = t.querySelectorAll("img.gallery-image"); for (let o of Array.from(r)) { let s = o.closest("p"); if (!s || !t.contains(s) || (s.textContent.trim() == "" && s.classList.add("no-text"), !s.classList.contains("no-text"))) continue; let d = o.parentElement.tagName == "A", m = o, a = document.createElement("figure"); if (a.style.setProperty("flex-grow", o.getAttribute("data-flex-grow") || "1"), a.style.setProperty("flex-basis", o.getAttribute("data-flex-basis") || "0"), d && (m = o.parentElement), m.parentElement.insertBefore(a, m), a.appendChild(m), o.hasAttribute("alt")) { let l = document.createElement("figcaption"); l.innerText = o.getAttribute("alt"), a.appendChild(l) } if (!d) { a.className = "gallery-image"; let l = document.createElement("a"); l.href = o.src, l.setAttribute("target", "_blank"), o.parentNode.insertBefore(l, o), l.appendChild(o) } } let i = t.querySelectorAll("figure.gallery-image"), n = []; for (let o of i) n.length ? o.previousElementSibling === n[n.length - 1] ? n.push(o) : n.length && (e.wrap(n), n = [o]) : n = [o]; n.length > 0 && e.wrap(n) } static wrap(t) { let r = document.createElement("div"); r.className = "gallery"; let i = t[0].parentNode, n = t[0]; i.insertBefore(r, n); for (let o of t) r.appendChild(o) } open(t) { let r = document.querySelector(".pswp"); new window.PhotoSwipe(r, window.PhotoSwipeUI_Default, this.items, { index: t, galleryUID: this.galleryUID, getThumbBoundsFn: n => { let o = this.items[n].el.getElementsByTagName("img")[0], s = window.pageYOffset || document.documentElement.scrollTop, c = o.getBoundingClientRect(); return { x: c.left, y: c.top + s, w: c.width } } }).init() } bindClick() { for (let [t, r] of this.items.entries()) r.el.querySelector("a").addEventListener("click", n => { n.preventDefault(), this.open(t) }) } }, b = g; var u = {}; if (localStorage.hasOwnProperty("StackColorsCache")) try { u = JSON.parse(localStorage.getItem("StackColorsCache")) } catch { u = {} } async function S(e, t, r) { if (!e) return await Vibrant.from(r).getPalette(); if (!u.hasOwnProperty(e) || u[e].hash !== t) { let i = await Vibrant.from(r).getPalette(); u[e] = { hash: t, Vibrant: { hex: i.Vibrant.hex, rgb: i.Vibrant.rgb, bodyTextColor: i.Vibrant.bodyTextColor }, DarkMuted: { hex: i.DarkMuted.hex, rgb: i.DarkMuted.rgb, bodyTextColor: i.DarkMuted.bodyTextColor } }, localStorage.setItem("StackColorsCache", JSON.stringify(u)) } return u[e] } var D = (e, t = 500) => { e.classList.add("transiting"), e.style.transitionProperty = "height, margin, padding", e.style.transitionDuration = t + "ms", e.style.height = e.offsetHeight + "px", e.offsetHeight, e.style.overflow = "hidden", e.style.height = "0", e.style.paddingTop = "0", e.style.paddingBottom = "0", e.style.marginTop = "0", e.style.marginBottom = "0", window.setTimeout(() => { e.classList.remove("show"), e.style.removeProperty("height"), e.style.removeProperty("padding-top"), e.style.removeProperty("padding-bottom"), e.style.removeProperty("margin-top"), e.style.removeProperty("margin-bottom"), e.style.removeProperty("overflow"), e.style.removeProperty("transition-duration"), e.style.removeProperty("transition-property"), e.classList.remove("transiting") }, t) }, q = (e, t = 500) => { e.classList.add("transiting"), e.style.removeProperty("display"), e.classList.add("show"); let r = e.offsetHeight; e.style.overflow = "hidden", e.style.height = "0", e.style.paddingTop = "0", e.style.paddingBottom = "0", e.style.marginTop = "0", e.style.marginBottom = "0", e.offsetHeight, e.style.transitionProperty = "height, margin, padding", e.style.transitionDuration = t + "ms", e.style.height = r + "px", e.style.removeProperty("padding-top"), e.style.removeProperty("padding-bottom"), e.style.removeProperty("margin-top"), e.style.removeProperty("margin-bottom"), window.setTimeout(() => { e.style.removeProperty("height"), e.style.removeProperty("overflow"), e.style.removeProperty("transition-duration"), e.style.removeProperty("transition-property"), e.classList.remove("transiting") }, t) }, B = (e, t = 500) => window.getComputedStyle(e).display === "none" ? q(e, t) : D(e, t); function v() { let e = document.getElementById("toggle-menu"); e && e.addEventListener("click", () => { document.getElementById("main-menu").classList.contains("transiting") || (document.body.classList.toggle("show-menu"), B(document.getElementById("main-menu"), 300), e.classList.toggle("is-active")) }) } function N(e, t, r) { var i = document.createElement(e); for (let n in t) if (n && t.hasOwnProperty(n)) { let o = t[n]; n == "dangerouslySetInnerHTML" ? i.innerHTML = o.__html : o === !0 ? i.setAttribute(n, n) : o !== !1 && o != null && i.setAttribute(n, o.toString()) } for (let n = 2; n < arguments.length; n++) { let o = arguments[n]; o && i.appendChild(o.nodeType == null ? document.createTextNode(o.toString()) : o) } return i } var w = N; var y = class { localStorageKey = "StackColorScheme"; currentScheme; systemPreferScheme; constructor(t) { this.bindMatchMedia(), this.currentScheme = this.getSavedScheme(), this.dispatchEvent(document.documentElement.dataset.scheme), t && this.bindClick(t), document.body.style.transition == "" && document.body.style.setProperty("transition", "background-color .3s ease") } saveScheme() { localStorage.setItem(this.localStorageKey, this.currentScheme) } bindClick(t) { t.addEventListener("click", r => { this.isDark() ? this.currentScheme = "light" : this.currentScheme = "dark", this.setBodyClass(), this.currentScheme == this.systemPreferScheme && (this.currentScheme = "auto"), this.saveScheme() }) } isDark() { return this.currentScheme == "dark" || this.currentScheme == "auto" && this.systemPreferScheme == "dark" } dispatchEvent(t) { let r = new CustomEvent("onColorSchemeChange", { detail: t }); window.dispatchEvent(r) } setBodyClass() { this.isDark() ? document.documentElement.dataset.scheme = "dark" : document.documentElement.dataset.scheme = "light", this.dispatchEvent(document.documentElement.dataset.scheme) } getSavedScheme() { let t = localStorage.getItem(this.localStorageKey); return t == "light" || t == "dark" || t == "auto" ? t : "auto" } bindMatchMedia() { window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", t => { t.matches ? this.systemPreferScheme = "dark" : this.systemPreferScheme = "light", this.setBodyClass() }) } }, E = y; function p(e) { let t; return () => { t && window.cancelAnimationFrame(t), t = window.requestAnimationFrame(() => e()) } } var O = ".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]", T = "#TableOfContents", L = "#TableOfContents li", k = "active-class"; function V(e, t) { let r = e.querySelector("a").offsetHeight, i = e.offsetTop - t.offsetHeight / 2 + r / 2 - t.offsetTop; i < 0 && (i = 0), t.scrollTo({ top: i, behavior: "smooth" }) } function U(e) { let t = {}; return e.forEach(r => { let n = r.querySelector("a").getAttribute("href"); n.startsWith("#") && (t[n.slice(1)] = r) }), t } function C(e) { let t = []; return e.forEach(r => { t.push({ id: r.id, offset: r.offsetTop }) }), t.sort((r, i) => r.offset - i.offset), t } function M() { let e = document.querySelectorAll(O); if (!e) { console.warn("No header matched query", e); return } let t = document.querySelector(T); if (!t) { console.warn("No toc matched query", T); return } let r = document.querySelectorAll(L); if (!r) { console.warn("No navigation matched query", L); return } let i = C(e), n = !1; t.addEventListener("mouseenter", p(() => n = !0)), t.addEventListener("mouseleave", p(() => n = !1)); let o, s = U(r); function c() { let m = document.documentElement.scrollTop || document.body.scrollTop, a; i.forEach(f => { m >= f.offset - 20 && (a = document.getElementById(f.id)) }); let l; a && (l = s[a.id]), a && !l ? console.debug("No link found for section", a) : l !== o && (o && o.classList.remove(k), l && (l.classList.add(k), n || V(l, t)), o = l) } window.addEventListener("scroll", p(c)); function d() { i = C(e), c() } window.addEventListener("resize", p(d)) } var $ = "a[href]"; function P() { document.querySelectorAll($).forEach(e => { e.getAttribute("href").startsWith("#") && e.addEventListener("click", r => { r.preventDefault(); let i = decodeURI(e.getAttribute("href").substring(1)), n = document.getElementById(i), o = n.getBoundingClientRect().top - document.documentElement.getBoundingClientRect().top; window.history.pushState({}, "", e.getAttribute("href")), scrollTo({ top: o, behavior: "smooth" }) }) }) } var x = { 3 | init: () => { 4 | v(); let e = document.querySelector(".article-content"); e && (new b(e), P(), M()); let t = document.querySelector(".article-list--tile"); t && new IntersectionObserver(async (s, c) => { 5 | s.forEach(d => { 6 | if (!d.isIntersecting) return; c.unobserve(d.target), d.target.querySelectorAll("article.has-image").forEach(async a => { 7 | let l = a.querySelector("img"), f = l.src, H = l.getAttribute("data-key"), I = l.getAttribute("data-hash"), A = a.querySelector(".article-details"), h = await S(H, I, f); A.style.background = ` 8 | linear-gradient(0deg, 9 | rgba(${h.DarkMuted.rgb[0]}, ${h.DarkMuted.rgb[1]}, ${h.DarkMuted.rgb[2]}, 0.5) 0%, 10 | rgba(${h.Vibrant.rgb[0]}, ${h.Vibrant.rgb[1]}, ${h.Vibrant.rgb[2]}, 0.75) 100%)` 11 | }) 12 | }) 13 | }).observe(t); let r = document.querySelectorAll(".article-content div.highlight"), i = "Copy", n = "Copied!"; r.forEach(o => { let s = document.createElement("button"); s.innerHTML = i, s.classList.add("copyCodeButton"), o.appendChild(s); let c = o.querySelector("code[data-lang]"); c && s.addEventListener("click", () => { navigator.clipboard.writeText(c.textContent).then(() => { s.textContent = n, setTimeout(() => { s.textContent = i }, 1e3) }).catch(d => { alert(d), console.log("Something went wrong", d) }) }) }), new E(document.getElementById("dark-mode-toggle")) 14 | } 15 | }; window.addEventListener("load", () => { setTimeout(function () { x.init() }, 0) }); window.Stack = x; window.createElement = w; 16 | })(); 17 | /*! 18 | * Hugo Theme Stack 19 | * 20 | * @author: Jimmy Cai 21 | * @website: https://jimmycai.com 22 | * @link: https://github.com/CaiJimmy/hugo-theme-stack 23 | */ -------------------------------------------------------------------------------- /js/photoswipe-ui-default.min.js: -------------------------------------------------------------------------------- 1 | /*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08 2 | * http://photoswipe.com 3 | * Copyright (c) 2019 Dmitry Semenov; */ 4 | !function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipeUI_Default=b()}(this,function(){"use strict";var a=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=this,w=!1,x=!0,y=!0,z={barsSize:{top:44,bottom:"auto"},closeElClasses:["item","caption","zoom-wrap","ui","top-bar"],timeToIdle:4e3,timeToIdleOutside:1e3,loadingIndicatorDelay:1e3,addCaptionHTMLFn:function(a,b){return a.title?(b.children[0].innerHTML=a.title,!0):(b.children[0].innerHTML="",!1)},closeEl:!0,captionEl:!0,fullscreenEl:!0,zoomEl:!0,shareEl:!0,counterEl:!0,arrowEl:!0,preloaderEl:!0,tapToClose:!1,tapToToggleControls:!0,clickToCloseNonZoomable:!0,shareButtons:[{id:"facebook",label:"Share on Facebook",url:"https://www.facebook.com/sharer/sharer.php?u={{url}}"},{id:"twitter",label:"Tweet",url:"https://twitter.com/intent/tweet?text={{text}}&url={{url}}"},{id:"pinterest",label:"Pin it",url:"http://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{text}}"},{id:"download",label:"Download image",url:"{{raw_image_url}}",download:!0}],getImageURLForShare:function(){return a.currItem.src||""},getPageURLForShare:function(){return window.location.href},getTextForShare:function(){return a.currItem.title||""},indexIndicatorSep:" / ",fitControlsWidth:1200},A=function(a){if(r)return!0;a=a||window.event,q.timeToIdle&&q.mouseUsed&&!k&&K();for(var c,d,e=a.target||a.srcElement,f=e.getAttribute("class")||"",g=0;g-1&&(c.onTap(),d=!0);if(d){a.stopPropagation&&a.stopPropagation(),r=!0;var h=b.features.isOldAndroid?600:30;s=setTimeout(function(){r=!1},h)}},B=function(){return!a.likelyTouchDevice||q.mouseUsed||screen.width>q.fitControlsWidth},C=function(a,c,d){b[(d?"add":"remove")+"Class"](a,"pswp__"+c)},D=function(){var a=1===q.getNumItemsFn();a!==p&&(C(d,"ui--one-slide",a),p=a)},E=function(){C(i,"share-modal--hidden",y)},F=function(){return y=!y,y?(b.removeClass(i,"pswp__share-modal--fade-in"),setTimeout(function(){y&&E()},300)):(E(),setTimeout(function(){y||b.addClass(i,"pswp__share-modal--fade-in")},30)),y||H(),!1},G=function(b){b=b||window.event;var c=b.target||b.srcElement;return a.shout("shareLinkClick",b,c),!!c.href&&(!!c.hasAttribute("download")||(window.open(c.href,"pswp_share","scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,top=100,left="+(window.screen?Math.round(screen.width/2-275):100)),y||F(),!1))},H=function(){for(var a,b,c,d,e,f="",g=0;g