SCSS 常用语法规则详解

1. 变量 (Variables) // 定义变量 $primary-color: #3498db; $font-size: 16px; $font-family: 'Arial', sans-serif; // 使用变量 body { color: $primary-color; font-size: $font-size; font-family: $font-family; } 解释:变量用于存储可重用的值,如颜色、字体、尺寸等,便于统一管理和维护。 编译后的 CSS: body { color: #3498db; font-size: 16px; font-family: 'Arial', sans-serif; } 2. 嵌套 (Nesting) nav { ul { margin: 0; padding: 0; list-style: none; li { display: inline-block; a { text-decoration: none; padding: 5px 10px; &:hover { color: red; } } } } } ...

2025-11-04 · 4 min · Duke Yin

GitHub Action: 自动发布WordPress主题插件新版本

主要功能 当主题或插件修改提交commit 并 push到Github时,自动打包主题插件发布一个Realease 判断当前版本号,如果与之前版本号相同则不发布 .releaseignore文件设置忽略的文件或文件夹,不打包进release 主题 必须按照WordPress主题开发规范,在style.css顶部注释版本号(Version:) 文件位置: .github/workflows/release-on-version-change.yml 内容: name: Release on version change on: push: branches: - main - master permissions: contents: write packages: write jobs: release: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Get previous commit hash id: prev run: | echo "prev_commit=$(git rev-parse HEAD^)" >> $GITHUB_ENV - name: Extract current version from style.css id: current_version run: | version=$(grep -E '^(Version:|版本:)' style.css | sed 's/.*[::] *//') if [ -z "$version" ]; then echo "❌ 未能在当前 style.css 中找到版本号" exit 1 fi echo "current_version=$version" >> $GITHUB_ENV echo "当前版本: $version" - name: Extract previous version from last commit id: prev_version run: | git show ${{ env.prev_commit }}:style.css > old_style.css || true version=$(grep -E '^(Version:|版本:)' old_style.css | sed 's/.*[::] *//') echo "prev_version=$version" >> $GITHUB_ENV echo "上一个版本: $version" - name: Compare versions id: compare run: | if [ "${{ env.current_version }}" != "${{ env.prev_version }}" ]; then echo "version_changed=true" >> $GITHUB_ENV echo "🔄 版本变化: ${{ env.prev_version }} → ${{ env.current_version }}" else echo "version_changed=false" >> $GITHUB_ENV echo "ℹ️ 版本未变化,无需发布" fi - name: Stop if version not changed if: env.version_changed == 'false' run: exit 0 - name: Check if tag already exists id: tag_check run: | if git rev-parse "v${{ env.current_version }}" >/dev/null 2>&1; then echo "tag_exists=true" >> $GITHUB_ENV echo "⚠️ Tag v${{ env.current_version }} 已存在,跳过发布" else echo "tag_exists=false" >> $GITHUB_ENV fi - name: Stop if tag already exists if: env.tag_exists == 'true' run: exit 0 - name: Create Git tag if: env.tag_exists == 'false' run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git tag -a "v${{ env.current_version }}" -m "Release v${{ env.current_version }}" git push origin "v${{ env.current_version }}" - name: Create ZIP package (with ignore list) run: | ZIP_FILE="${{ github.event.repository.name }}-${{ env.current_version }}.zip" EXCLUDE_ARGS="-x '*.git*' '.github/*'" if [ -f ".releaseignore" ]; then echo "🧾 使用 .releaseignore 过滤以下内容:" while IFS= read -r line; do if [ -n "$line" ] && [[ ! "$line" =~ ^# ]]; then echo " - $line" EXCLUDE_ARGS="$EXCLUDE_ARGS '$line'" fi done < .releaseignore else echo "⚠️ 未找到 .releaseignore,使用默认排除规则。" fi eval zip -r "$ZIP_FILE" . $EXCLUDE_ARGS echo "✅ 打包完成:$ZIP_FILE" - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: v${{ env.current_version }} name: "Release v${{ env.current_version }}" body: | 🎉 自动发布版本 v${{ env.current_version }} 🔖 来自 commit: ${{ github.sha }} files: ${{ github.event.repository.name }}-${{ env.current_version }}.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 文件位置: ...

2025-11-03 · 4 min · Duke Yin

安装私有语言聊天大模型平台Ollama

Ollama是一个开源大语言模型,可以作为后端 运行不同的LLM。 安装Ollama 我的服务器系统是Linux Debian 所以使用Ollama官方的一条命令就能安装: curl -fsSL https://ollama.com/install.sh | sh Mac和Windows参考官网: https://ollama.com/download/ 过程中会连接github,下载ollama-linux-amd64.tgz,大约1.78g 安装完成后,会提示在本地 127.0.0.1:11434 运行了ollama实例。 测试ollama是否已经安装成功: ollama -v 如果输出版本信息,则说明安装成功。 配置文件默认存在于: /etc/systemd/system/ollama.service 可以使用你熟悉的文本编辑工具进行编辑修改。 如果你的WebUI和Ollama位于不同的服务器,或者要使用第三方工具调用Ollama,需要远程访问Ollama的API的话,需要在配置的service处增加一条: [Service] Environment="OLLAMA_HOST=0.0.0.0" Environment="OLLAMA_ORIGINS=*" 如果指定使用某GPU的话,需要增加: Environment="OLLAMA_DEVICE=cuda" Environment="CUDA_VISIBLE_DEVICES=0" 然后重载并重启Ollama sudo systemctl daemon-reload sudo systemctl restart ollama 如果在Ollama本机安装WebUI则不用,暴露外网会增加服务器资源被盗用的风险。 附录Ollama常用环境变量: 参数标识与配置OLLAMA_MODELS表示模型文件的存放目录,默认目录为当前用户目录即 C:\Users%username%.ollama\models Windows 系统 建议不要放在C盘,可放在其他盘(如 E:\ollama\models)OLLAMA_HOST表示ollama 服务监听的网络地址,默认为127.0.0.1 如果想要允许其他电脑访问 Ollama(如局域网中的其他电脑),建议设置成 0.0.0.0,如果只允许局域网,建议设置为本机的局域网IP,例如 192.168.2.100OLLAMA_PORT表示ollama 服务监听的默认端口,默认为11434 如果端口有冲突,可以修改设置成其他端口(如8080等)OLLAMA_ORIGINS表示HTTP 客户端的请求来源,使用半角逗号分隔列表 如果本地使用不受限制,可以设置成星号 *OLLAMA_KEEP_ALIVE表示大模型加载到内存中后的存活时间,默认为5m即 5 分钟 (如纯数字300 代表 300 秒,0 代表处理请求响应后立即卸载模型,任何负数则表示一直存活) 建议设置成 24h ,即模型在内存中保持 24 小时,提高访问速度OLLAMA_NUM_PARALLEL表示请求处理的并发数量,默认为1 (即单并发串行处理请求) 建议按照实际需求进行调整OLLAMA_MAX_QUEUE表示请求队列长度,默认值为512 建议按照实际需求进行调整,超过队列长度的请求会被抛弃OLLAMA_DEBUG表示输出 Debug 日志,应用研发阶段可以设置成1 (即输出详细日志信息,便于排查问题)OLLAMA_MAX_LOADED_MODELS表示最多同时加载到内存中模型的数量,默认为1 (即只能有 1 个模型在内存中) 常用命令: ...

2025-09-30 · 1 min · Duke Yin

Vmware Workstation和ESXi的下载地址

这两个软件目前都官方免费了,但是下载地址非常不好找,于是备忘 Vmware Workstation VMware vSphere Hypervisor (ESXi)

2025-05-18 · 1 min · Duke Yin

WordPress上传图片自动转换为WebP或AVIF

<?php /* Plugin Name: AVIF/WebP Image Converter Description: Automatically converts uploaded images to AVIF/WebP format with alpha channel support. Version: 1.0 Author: Your Name */ // 检测服务器支持情况 function awc_check_support() { $support = [ 'gd' => [ 'installed' => extension_loaded('gd'), 'avif' => false, 'webp' => false, ], 'imagick' => [ 'installed' => extension_loaded('imagick'), 'avif' => false, 'webp' => false, ] ]; // 检测GD库支持 if ($support['gd']['installed']) { $gd_info = gd_info(); $support['gd']['webp'] = isset($gd_info['WebP Support']) && $gd_info['WebP Support']; $support['gd']['avif'] = function_exists('imageavif') && defined('IMG_AVIF') && (imagetypes() & IMG_AVIF); } // 检测Imagick支持 if ($support['imagick']['installed']) { try { $imagick = new Imagick(); $formats = array_map('strtoupper', $imagick->queryFormats()); $support['imagick']['avif'] = in_array('AVIF', $formats); $support['imagick']['webp'] = in_array('WEBP', $formats); } catch (Exception $e) { error_log('Imagick检测失败: ' . $e->getMessage()); } } return $support; } // 后台显示检测结果 function awc_add_admin_page() { add_submenu_page( 'tools.php', '图片格式支持检测', '图片格式支持', 'manage_options', 'image-format-support', 'awc_display_support_page' ); } add_action('admin_menu', 'awc_add_admin_page'); function awc_display_support_page() { $support = awc_check_support(); ?> <div class="wrap"> <h1>服务器图片格式支持检测</h1> <h2>GD 库</h2> <p>已安装: <?php echo $support['gd']['installed'] ? '✅' : '❌'; ?></p> <p>AVIF 支持: <?php echo $support['gd']['avif'] ? '✅' : '❌'; ?></p> <p>WebP 支持: <?php echo $support['gd']['webp'] ? '✅' : '❌'; ?></p> <h2>Imagick</h2> <p>已安装: <?php echo $support['imagick']['installed'] ? '✅' : '❌'; ?></p> <p>AVIF 支持: <?php echo $support['imagick']['avif'] ? '✅' : '❌'; ?></p> <p>WebP 支持: <?php echo $support['imagick']['webp'] ? '✅' : '❌'; ?></p> <p>转换策略:优先使用 <?php if($support['gd']['avif']){ echo 'GD转AVIF'; }elseif($support['gd']['webp']){ echo 'GD转WEBP'; }elseif($support['imagick']['avif']){ echo 'Imagick转换AVIF'; }elseif($support['imagick']['webp']){ echo 'Imagick转换WebP'; } ?></p> </div> <?php } // 处理图片上传 add_filter('wp_handle_upload_prefilter', 'awc_convert_image'); function awc_convert_image($file) { // 仅处理图片文件 if (!str_starts_with($file['type'], 'image/')) { return $file; } $support = awc_check_support(); $tmp_path = $file['tmp_name']; $converted = false; $target_format = null; // 确定目标格式和转换方式 if (($support['gd']['avif'] || $support['imagick']['avif'])) { $target_format = 'avif'; } elseif (($support['gd']['webp'] || $support['imagick']['webp'])) { $target_format = 'webp'; } if (!$target_format) return $file; // 优先使用GD库 if ($target_format === 'avif' && $support['gd']['avif']) { $converted = awc_convert_with_gd($tmp_path, 'avif'); } elseif ($target_format === 'avif' && $support['imagick']['avif']) { $converted = awc_convert_with_imagick($tmp_path, 'avif'); } if (!$converted && $target_format === 'webp') { if ($support['gd']['webp']) { $target_format = 'webp'; $converted = awc_convert_with_gd($tmp_path, 'webp'); } elseif ($support['imagick']['webp']) { $target_format = 'webp'; $converted = awc_convert_with_imagick($tmp_path, 'webp'); } } if ($converted) { // 更新文件信息 $file['name'] = preg_replace('/\.(jpe?g|png|gif)$/i', '.' . $target_format, $file['name']); $file['type'] = ($target_format === 'avif') ? 'image/avif' : 'image/webp'; } return $file; } // GD库转换函数 function awc_convert_with_gd($path, $format) { try { $image = null; $mime = mime_content_type($path); switch ($mime) { case 'image/jpeg': $image = imagecreatefromjpeg($path); break; case 'image/png': $image = imagecreatefrompng($path); imagealphablending($image, false); imagesavealpha($image, true); break; case 'image/gif': $image = imagecreatefromgif($path); break; default: return false; } if (!$image) return false; $success = false; switch ($format) { case 'avif': $success = imageavif($image, $path, 80); break; case 'webp': $success = imagewebp($image, $path, 80); break; } imagedestroy($image); return $success; } catch (Exception $e) { error_log('GD转换错误: ' . $e->getMessage()); return false; } } // Imagick转换函数 function awc_convert_with_imagick($path, $format) { try { $imagick = new Imagick($path); $format = strtoupper($format); // 保留透明度 $imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); $imagick->setFormat($format); $imagick->setImageCompressionQuality(80); // 删除EXIF数据 $imagick->stripImage(); $success = $imagick->writeImage($path); $imagick->clear(); return $success; } catch (Exception $e) { error_log('Imagick转换错误: ' . $e->getMessage()); return false; } }

2025-04-23 · 3 min · Duke Yin

PHP判断网页是否在iframe加载

if( isset($_SERVER['HTTP_SEC_FETCH_DEST']) && $_SERVER['HTTP_SEC_FETCH_DEST'] == 'iframe' ) { //网页被嵌入iframe }else{ //网页没有被嵌入iframe }

2025-04-22 · 1 min · Duke Yin

Emby API获取媒体库信息

通过Emby内置的API可以远程获取最近添加的电影和剧集。 如下: https://[EmbyServerURL]:[Port]/ emby/Users/ [UserKey] /Items ?IncludeItemTypes=Movie // series,tv &Recursive=True &ExcludeLocationTypes=Virtual &Fields=DateCreated,PremiereDate,Overview,CommunityRating &Limit=5 &GroupItems=false &SortBy=DateCreated &SortOrder=Descending &format=json &api_key=[APIKey] IncludeItemTypes可以包含Movie电影,Series剧集,tv分集,可以单个也可以多个逗号分隔。 通过返回的id字段,可获取名称,添加时间,海报,背景图片 海报: https://[EmbyServerURL]:[Port]/emby/Items/[id]/Images/Primary?maxHeight=300&maxWidth=200&quality=90 背景图: https://[EmbyServerURL]:[Port]/Items/[id]/Images/Backdrop?maxHeight=360&maxWidth=640&quality=90 在远程调用时,不建议使用前端语言,会暴露APIkey,写在服务端可以安全获取。

2025-04-17 · 1 min · Duke Yin

TMDB Image sizes

{ "base_url": "http://image.tmdb.org/t/p/", "secure_base_url": "https://image.tmdb.org/t/p/", "backdrop_sizes": [ "w300", "w780", "w1280", "original" ], "logo_sizes": [ "w45", "w92", "w154", "w185", "w300", "w500", "original" ], "poster_sizes": [ "w92", "w154", "w185", "w342", "w500", "w780", "original" ], "profile_sizes": [ "w45", "w185", "h632", "original" ], "still_sizes": [ "w92", "w185", "w300", "original" ] }

2025-04-08 · 1 min · Duke Yin

HomeAssistant实用自定义模板

Custom Templates 日夜晨昏传感器 sensor: - platform: template sensors: period_of_day: friendly_name: 'period of the day' value_template: >- {% if (as_timestamp(states.sun.sun.attributes.next_dusk)) - (as_timestamp(states.sun.sun.attributes.next_setting)) < 0 %} dusk {% elif (as_timestamp(states.sun.sun.attributes.next_rising)) - (as_timestamp(states.sun.sun.attributes.next_dawn)) < 0 %} dawn {% elif (states.sun.sun.attributes.elevation) < -4 %} night {% else %} day {% endif %} icon_template: >- {% if (as_timestamp(states.sun.sun.attributes.next_dusk)) - (as_timestamp(states.sun.sun.attributes.next_setting)) < 0 %} mdi:weather-sunset-down {% elif (as_timestamp(states.sun.sun.attributes.next_rising)) - (as_timestamp(states.sun.sun.attributes.next_dawn)) < 0 %} mdi:weather-sunset-up {% elif (states.sun.sun.attributes.elevation) < -4 %} mdi:weather-night {% else %} mdi:weather-sunny {% endif %} attribute_templates: color: > {% if is_state('sensor.period_of_day', 'dusk') %} lime {% elif is_state('sensor.period_of_day', 'dawn') %} orange {% elif is_state('sensor.period_of_day', 'night') %} teal {% else %} yellow {% endif %} 允许本地网络反向代理 # Allow reserved proxy from local network http: use_x_forwarded_for: true trusted_proxies: - 192.168.X.0/24 # Replace X for your local network range number eg. 192.168.3.0

2025-02-04 · 1 min · Duke Yin

智能电表

ESPHome电表 接线方式: 进线口:220v火线 套电流互感器 出线口,接零火线供电 配网:通电后连接“esp-meter”wifi热点。 HomeAssistant需添加esphome集成。 MQTT电表 连接wifi热点配网 meter12-1 加密秘钥: api: encryption: key: "D+/93MW6+EUbI3pracfDhI6/l0PgpQdnxxjezKn4T88=" 如果有密码 就是admin admin

2025-01-14 · 1 min · Duke Yin