macOS 终端配置:Ghostty + Oh My Zsh + Starship

这是一套偏日常开发使用的 macOS 终端配置:使用 Ghostty 作为终端模拟器,使用 Oh My Zsh 管理 Zsh 配置和插件,最后用 Starship 接管命令行提示符。 目标 Ghostty:终端应用,启动快,默认配置已经足够可用。 Oh My Zsh:管理 Zsh 配置、插件和常用别名。 Oh My Zsh plugins:补全、建议、高亮、Git、sudo 等效率插件。 Starship:跨 shell 的命令行提示符,替代 Oh My Zsh 主题。 准备 确认当前 shell 是 Zsh: echo $SHELL zsh --version 如果当前不是 Zsh,可以切换为 macOS 自带的 Zsh: chsh -s /bin/zsh 安装 Homebrew 后,后续工具都可以统一用 brew 管理: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update 安装 Ghostty Ghostty 的 macOS 版本可以直接下载安装,也可以通过 Homebrew Cask 安装: brew install --cask ghostty 启动 Ghostty: ...

May 9, 2026 · 3 min · 631 words · Jiang Jun Jie

iOS 软件推荐

这是一份偏日常使用、效率、文件管理和轻量开发的 iPhone / iPad 软件推荐清单。iOS 上优先选择系统自带能力,其次再补充真正能改善工作流的第三方应用。 iPhone iPad

May 9, 2026 · 1 min · 8 words · Jiang Jun Jie

macOS 软件推荐

这是一份偏日常开发、办公和系统效率的 Mac 装机清单。优先推荐长期维护、安装方便、能补齐 macOS 使用短板的软件。 安装方式 推荐先安装 Homebrew,后续大部分软件都可以用命令安装、升级和卸载。 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update 必装效率工具 开发工具 Visual Studio Code:轻量代码编辑器,扩展生态完整,适合前端、脚本和轻量项目。 JetBrains Toolbox:统一管理 IntelliJ IDEA、WebStorm、PyCharm、DataGrip 等 文件与媒体 IINA:现代化视频播放器,基于 mpv,适合播放本地视频和常见媒体格式。 LocalSend:局域网跨设备传文件,不依赖云端账号。 笔记与写作 Obsidian:Markdown 笔记工具,本地文件优先,适合知识库和长期资料整理。 沟通与办公 WeChat:微信。 Tencent Meeting:腾讯会议。 Feishu:飞书。 Microsoft 365:Word、Excel、PowerPoint、OneDrive。

May 9, 2026 · 1 min · 39 words · Jiang Jun Jie

Powerlevel10k

macOS 安装 Powerlevel10k Powerlevel10k 是一个 Zsh 主题,特点是速度快、可定制程度高,并且自带交互式配置向导。 准备 确认当前 shell 是 Zsh: echo $SHELL 如果输出不是 /bin/zsh,可以切换为 Zsh: chsh -s /bin/zsh 安装推荐字体 Powerlevel10k 推荐使用 MesloLGS NF 字体,否则图标可能显示为乱码或方块。 使用 Homebrew 安装: brew install --cask font-meslo-lg-nerd-font 安装完成后,在终端应用里把字体切换为 MesloLGS NF。 常见终端字体设置位置: Terminal.app:设置 -> 描述文件 -> 文本 -> 字体 iTerm2:Settings -> Profiles -> Text -> Font VS Code:设置 terminal.integrated.fontFamily 为 MesloLGS NF 方式一:使用 Homebrew 安装 brew install powerlevel10k 安装完成后,把主题加载语句写入 ~/.zshrc: echo 'source $(brew --prefix)/share/powerlevel10k/powerlevel10k.zsh-theme' >> ~/.zshrc 重新加载配置: ...

May 9, 2026 · 1 min · 206 words · Jiang Jun Jie

Mediamtx

mediamtx Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish and proxy video and audio streams. https://github.com/bluenviron/mediamtx

September 13, 2023 · 1 min · 28 words · Jiang Jun Jie

Wsl

Issues windows unix 编码问题 sudo apt update sudo apt install dos2unix dos2unix [filename] 导出、注销、导入 WSL WSL 发行版可以通过 wsl --export 导出成 tar 文件,再用 wsl --import 导入到新的目录。这个方法适合备份、迁移磁盘位置,或者重建 Docker Desktop 的 WSL 数据。 先查看当前发行版名称: wsl --list --verbose 操作前建议关闭所有 WSL 实例和 Docker Desktop: wsl --shutdown 准备备份和导入目录: mkdir E:\wsl\backup mkdir E:\wsl\docker-desktop-data mkdir E:\wsl\docker-desktop mkdir E:\wsl\Ubuntu 命令格式 导出: wsl --export <发行版名称> <tar文件路径> 注销: wsl --unregister <发行版名称> 导入: wsl --import <发行版名称> <安装目录> <tar文件路径> 注意:wsl --unregister 会删除对应发行版的数据。确认 tar 文件已经导出成功后,再执行注销。 迁移 docker-desktop-data docker-desktop-data 保存 Docker Desktop 的镜像、容器、volume 等数据。迁移前一定要先退出 Docker Desktop。 wsl --shutdown wsl --export docker-desktop-data E:\wsl\backup\docker-desktop-data.tar wsl --unregister docker-desktop-data wsl --import docker-desktop-data E:\wsl\docker-desktop-data E:\wsl\backup\docker-desktop-data.tar 迁移 docker-desktop docker-desktop 是 Docker Desktop 使用的 WSL 发行版。顺序同样是先导出,再注销,最后导入。 ...

August 30, 2023 · 1 min · 158 words · Jiang Jun Jie

Nacos

https://github.com/nacos-group/nacos-docker

April 25, 2023 · 1 min · word · Jiang Jun Jie

Jira

https://github.com/haxqer/jira

April 25, 2023 · 1 min · word · Jiang Jun Jie

Jenkins

docker-compose.yml version: '3.3' services: jenkins: image: jenkins/jenkins:lts restart: unless-stopped privileged: true ports: - 8081:8080 container_name: jenkins volumes: - jenkins_data:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock - /usr/local/bin/docker:/usr/local/bin/docker volumes: jenkins_data: external: false

April 25, 2023 · 1 min · 27 words · Jiang Jun Jie

Hik H5player

vue 项目中使用hik h5player 下载海康h5player安装包 public 文件夹下新建hik_video文件夹,将安装包中的bin文件夹复制到hik_video文件夹下 public 文件夹下index.html。添加如下代码 <script type="text/javascript" src="<%= BASE_URL %>hik_video/h5player.min.js"></script> src\assets 文件夹下新建hik_video文件夹,将安装包中的bin文件夹复制到hik_video文件夹下 src\components 文件夹下新建HikH5Player文件夹,创建index.vue文件 <template> <div id="player" style="width: 800px;height: 600px;"></div> </template> <script> import '@/assets/hik_video/h5player.min.js' import {getPreviewUrl, getPlaybackUrl} from '@/api/data/camera' export default { name: 'hik-h5player', data(){ return { //播放器对象 player: null, splitNum: 1, mode: 1, } }, mounted() { this.createPlayer() this.init() }, methods: { init() { // 设置播放容器的宽高并监听窗口大小变化 window.addEventListener('resize', () => { this.player.JS_Resize() }) }, /** * 初始化播放器 */ createPlayer() { this.player = new window.JSPlugin({ // 需要英文字母开头 必填 szId: 'player', // 必填,引用H5player.min.js的js相对路径 szBasePath: './hik_video', // 当容器div#play_window有固定宽高时,可不传iWidth和iHeight,窗口大小将自适应容器宽高 iWidth: 600, iHeight: 400, // 分屏播放,默认最大分屏4*4 iMaxSplit: 16, iCurrentSplit: this.splitNum, // 样式 oStyle: { border: '#343434', borderSelect: '#FFCC00', background: '#000' } }) // 事件回调绑定 this.player.JS_SetWindowControlCallback({ windowEventSelect: function (iWndIndex) { //插件选中窗口回调 console.log('windowSelect callback: ', iWndIndex); }, pluginErrorHandler: function (iWndIndex, iErrorCode, oError) { //插件错误回调 console.log('pluginError callback: ', iWndIndex, iErrorCode, oError); }, windowEventOver: function (iWndIndex) { //鼠标移过回调 //console.log(iWndIndex); }, windowEventOut: function (iWndIndex) { //鼠标移出回调 //console.log(iWndIndex); }, windowEventUp: function (iWndIndex) { //鼠标mouseup事件回调 //console.log(iWndIndex); }, windowFullCcreenChange: function (bFull) { //全屏切换回调 console.log('fullScreen callback: ', bFull); }, firstFrameDisplay: function (iWndIndex, iWidth, iHeight) { //首帧显示回调 console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight); }, performanceLack: function () { //性能不足回调 console.log('performanceLack callback: '); } }); }, /** * 窗口分屏 */ arrangeWindow(splitNum) { this.splitNum = splitNum this.player.JS_ArrangeWindow(splitNum).then( () => { console.log(`arrangeWindow to ${splitNum}x${splitNum} success`) }, e => { console.error(e) } ) }, /** * 全屏 */ wholeFullScreen() { this.player.JS_FullScreenDisplay(true).then( () => { console.log(`wholeFullScreen success`) }, e => { console.error(e) } ) }, /** * 获取取流链接 * @returns {*} */ realplay(code,transmode,index) { let {player, mode} = this if(index === null || index === undefined){ index = player.currentWindowIndex } if(transmode === null || transmode === undefined){ transmode = 1 } const param = { 'cameraIndexCode': code, 'transmode': transmode } getPreviewUrl(param).then(response => { let playURL =response.data.url this.player.JS_Play(playURL, {playURL, mode}, index).then( () => { console.log('realplay success') }, e => { console.error(e) } ) }); }, stopPlay() { this.player.JS_Stop(this.player.currentWindowIndex).then( () => { console.log('stop realplay success') }, e => { console.error(e) } ) }, stopAllPlay() { this.player.JS_StopRealPlayAll().then( () => { console.log('stopAllPlay success') }, e => { console.error(e) } ) }, /** * 获取回放链接 2021-06-29T00:00:00Z * @returns {AxiosPromise} */ playback(code,transmode,startTime,endTime) { startTime += '+08:00' endTime += '+08:00' let { player, mode } = this, index = player.currentWindowIndex //todo const param = { "cameraIndexCode": code, "startTime": startTime, "endTime": endTime, 'protocol': 'ws', "transmode": transmode } getPlaybackUrl(param).then(response => { let playURL =response.data.url this.player.JS_Play(playURL, { playURL, mode }, index, startTime, endTime).then( () => { console.log('playbackStart success') }, e => { console.error(e) } ) }); }, playbackPause() { this.player.JS_Pause(this.player.currentWindowIndex).then( () => { console.log('playbackPause success') }, e => { console.error(e) } ) }, playbackResume() { this.player.JS_Resume(this.player.currentWindowIndex).then( () => { console.log('playbackResume success') }, e => { console.error(e) } ) }, playbackSlow(){ this.player.JS_Slow(this.player.currentWindowIndex).then( rate => { console.log('playbackSlow success. rate=' + rate) }, e => { console.error(e) } ) }, playbackFast(){ this.player.JS_Fast(this.player.currentWindowIndex).then( rate => { console.log('playbackFast success. rate=' + rate) }, e => { console.error(e) } ) } } } </script> //获取摄像机预览地址 export function getPreviewUrl(data){ return request({ url: '/data/camera/getPreviewUrl', method: 'post', data: data }) } //获取摄像机录像地址 export function getPlaybackUrl(data){ return request({ url: '/data/camera/getPlaybackUrl', method: 'post', data: data }) } 使用海康openapi获取摄像机的ws视频流地址 记录问题 海康h5player插件,需要海康服务器特定版本的流媒体网关,否则无法使用。 不支持大华摄像机,会报错 jsdecoder1.0 unsupport dahua video ...

April 13, 2023 · 3 min · 518 words · Jiang Jun Jie