创业的第三个春天

创业的第三个春天

如果你是第一次看这个系列的文章,可从第一《创业的第一个春天》 看起。 创业第三个年头了,娃已经长大到会问我今天还有多少 issues 没改完。 博客端 将 Solo、Pipe 完整接入社区,文章浏览计数和评论使用了社区统一提供的服务组件: uvstat:实现浏览、评论统计 vcomment:实现评论集成展现 另外,Solo 实现了静态化用法,可以生成导出静态站点,方便用户部署到 GitHub Pages 上。 笔记端 链滴笔记实现了初版,目前已经基本满足了我个人在本地 Markdown 编辑排版的需要: 有的长文需要好几天才能写完,本地陆续写好后再粘贴到线上发布。以前主要用有道云笔记,现在已经迁移到链滴笔记 记一些琐碎的记录,比如常用命令、TODO 后面我们将开始实现导出静态化站点功能,逐步集成社区,作为社区的笔记端节点。 社区端 暗黑模式 黑客派上线了暗黑模式,已经支持全部主题。具体的主题模式有三种:明亮、暗黑和随日出日落自动切换。为了“随日出日落自动切换”,我整理了中国城市经纬度数据,有需要的朋友可自取。 对于命名,之前稍微纠结过一下到底是叫“夜间模式”还是“暗黑模式”。....
Markdown 解析原理详解和 Markdown AST 描述

Markdown 解析原理详解和 Markdown AST 描述

概述 本文主要介绍 Markdown 引擎 Lute 的整体处理流程,并详细描述了 Markdown 抽象语法树结构。 编译原理 我们通过编译原理实现了 Lute ,大致步骤是预处理、词法分析、语法分析、代码生成这几个步骤。代码结构方面我们分为两部分,解析和渲染。 1// Markdown 将 markdown 文本字节数组处理为相应的 html 字节数组。name 参数仅用于标识文本,比如可传入 id 或者标题,也可以传入 ""。 2func (lute *Lute) Markdown(name string, markdown []byte) (html []byte) { 3 tree := parse.Parse(name, markdown, lute.Options) 4 renderer := render.NewHtmlRenderer(tree) 5 html = renderer.Render() 6 return 7} 解析过程用于从 Markdown 原文构造抽象语法树。 1// Parse 会将 markdown 原始文本字节数组解析为一颗语法树。 2f.......
Windows 10 搭建 TensorFlow 试玩 fast-style-transfer

Windows 10 搭建 TensorFlow 试玩 fast-style-transfer

本文适用于 TensorFlow 新手搭建试玩“图片快速风格迁移”,系统环境: Windows 10 显卡 NVIDIA GTX 1070 安装 TensorFlow 环境 用 Anaconda 来装环境可以省很多事。 安装 Anaconda,步骤中有两个选项记得勾上(添加 PATH 环境变量和使用 Anaconda 作为 Python 环境) 现在的 Anaconda 似乎已经内置了国内的几个镜像源,所以不用手动切换镜像,用默认配置即可 如果已经折腾过,可以考虑用如下命令恢复: conda config --remove-key channels conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes 如果你像我一样只是个入门玩家(并且平时没装过 Python 环境),那就不用搞 conda 的环境隔离了。直接执行 conda install tensorflow-gpu,....
创业的第二个冬天

创业的第二个冬天

如果你是第一次看这个系列的文章,可从第一篇《创业的第一个春天》[^1] 看起。 认真积累,别走捷径。 CDN 盗刷 12 月份的某天,社区的 CDN 图床被人恶意刷了好几十 G 流量,虽然造成的损失就几十块钱,但我觉得这不是钱的问题,而是对我技术能力的嘲讽。通过分析日志,基本可以判断攻击来源是类似基于 Electron、Headless Chrome 的应用,因为这些攻击流量的来源 IP 大部分不是来自机房服务器。 随后我们通过七牛云提供的“回源鉴权”机制解决了这个问题,所有对图床 CDN 的请求都需要鉴权。判断依据是通过 Referer、IP,大致规则是禁止为空的 Referer,并且 IP 不能来自机房。顺手也加了防盗链,综合了博客端节点(Solo、Pipe)回调接口来判断请求是否是博客端发起的。 后来攻击者发现我们的应对措施后,就将流量转移到了攻击博客端节点,一些用户的博客被恶意请求,请求并不密集,攻击者企图慢慢消耗社区图床 CDN 以造成损失。这部分攻击流量后来也通过限流算法做了判断,最终将不合法的 IP 加入到社区 IP 黑名单[^2]中,博客端节点会定时获取这份黑名单列表.....
Solo 从设计到实现后记

Solo 从设计到实现后记

本文是《Solo 从设计到实现》的一个章节,该系列文章将介绍 Solo 这款 Java 博客系统是如何从无到有的,希望大家能通过它对 Solo 从设计到实现有个直观地了解、能为想参与贡献的人介绍清楚项目,也希望能为给重复发明重新定义博客系统的人做个参考 ❤️ 轮子和时间 “不要重复发明轮子”在程序员界的流行应该是从 Rod 发明 Spring 开始。他曾经在其著作《Without EJB》中使用了这个谚语,并阐述了 Spring 的诞生基于这一理念。 重复发明轮子最大的问题在于浪费时间。作为作者,浪费时间重复劳动;作为用户,浪费时间尝试。一个不好的轮子会严重浪费大家的时间,所以“不要重复发明轮子”主要针对的是那些不够圆的轮子而言。 新轮子肯定不会很圆。要么是设计问题,要么是工艺问题。设计问题最好在设计阶段就确认好,工艺问题可以花时间解决。 我觉得只要大家有时间,就尽可能发明轮子吧。无论是产品层面的优化改进,还是框架技术层面的创新尝试,我觉得都是值得的。作为普通程序员,浪费一些时间并没有什么大不了的,相信勇于尝试并坚持下去,一定能够改变一些事情的。 成功的开源 什么是成功 一个开源项目.....
搭建 GitHub 镜像仓库

搭建 GitHub 镜像仓库

需求背景 国内访问 GitHub 仓库实在太慢,项目主要提交者也就我和 V,外加以后打算自己建立仓库,所以决定在新项目上试试。 将 GitHub 仓库作为镜像仓库,主库设在自建的服务器上。这样既能获得 GitHub 协作特性(Issues、PR、Actions、Release 等),开发时又能享受高速提交和拉取。 服务端 创建 Git 用户,配置 git-shell 等,然后配置 SSH 密钥,密钥分为两类: 客户端提交服务端用,修改 ~/.ssh/authorized_keys 服务端自动推送 GitHub,修改 ~/.ssh/id_rsa.pub。GitHub 上需要配置账号 SSH 或者仓库 Deploy Keys 切换到 Git 用户,并在 ~ 下创建仓库 mkdir sample.git && cd sample.git && Git clone --bare git@github.com:youraccount/sample.git 在 hooks 目录下创建名为 post-receive 的脚本: #!/bin/sh g....
链滴笔记路线图

链滴笔记路线图

设计原则 以文件系统为基础,不做私有格式封装 无需账号注册,可离线使用,无广告 v1 第一版主要目标是完成笔记应用基础功能,实现单机离线可用。 用户界面 分为三栏,参考有道云笔记: 文件夹 文件 编辑区 功能导航 通过 double shift 弹出功能搜索导航对话框: 配置入口 功能入口 全文搜索 不做菜单导航,所有功能入口均通过搜索导航实现。 图片/文件 使用本地绝对路径,文件由用户自己组织管理。 配置项 自动检查更新 Lute 相关设置 元数据 应用元数据保存在 ~/.liandi/conf.json ,记录已经打开的文件夹、各种配置项等 搜索索引缓存 ~/.liandi/index/ v2 第二版的主要目标是更好看的界面以及接入 B3log 分布式社区,实现分享互动。 用户界面 多主题切换,实现暗黑主题。 接入社区 作为 B3log 分布式社区的笔记端节点接入社区网络,实现文章推送和评论互动。 导出站点 支持生成导出静态站点,包含多款主题。 编辑器 实现 Typora 保留 Markdown 标记符的实时渲染模式。 架构设计 欢迎参与讨论,让我们一起来打造....
关于所见即所得 Markdown 编辑器的讨论

关于所见即所得 Markdown 编辑器的讨论

在 GitHub 上搜索 “Markdown editor”,会发现几乎所有流行的 Markdown 编辑器项目都会有被人提类似这样一个特性请求: 能否像 Typora 那样进行实时渲染? 这是一个有趣的现象,但几乎所有的项目都没实现,我翻了这些项目下面的 issue 讨论,作者们大致认为: 所见即所得违反了 Markdown 设计用意 设计上没有保证一致性,比如列表、块引用和标题就无法展开标记符;有的操作只能依赖鼠标或者快捷键,比如表格扩展行列 实现工作量较大,还有优先级更高的任务需要去做 太难了 其中 VNote 提出的“折中方案”,即图片、图表和公式进行实时渲染,其他元素则进行 Markdown 语法高亮。这个方案看似折中,但实则更严重地破坏了一致性,会让用户更容易分心。老外们的项目似乎很看重“设计一致性”,只要逻辑上有点瑕疵他们都不接受。而国人大部分(包括我)都是实用主义者,用着爽就行,管那么多干啥。 Vditor 虽然还不那么流行,但是也被提过。本来我和 @Vanessa 是想做好所见即所得就行了,这样不熟悉 Markdown 的人也能用。当我翻遍了现有 Markdo....
2019 用善意面对世界

2019 用善意面对世界

春夏秋 春、夏、秋。 明年就是《创》的第三年了,也将是最后一年更新该系列了,以后不定时有感随发。 Vditor 今年我和 V 主要精力花在了构建 Vditor 上,我们没有选择基于其他编辑器内核来做,而是从零开始写,包括 Markdown 解析渲染也撸了一遍,整个实现基本没有第三方依赖(除了支持图表、数学公式等)。 对于大部分开发者来说,提供给用户使用的编辑器是能用就凑合着用,因为编辑器候选品不多,自己造轮子的话工作量不小,即使碰到有需要更改的地方也有办法: 通过编辑器本身的扩展机制 在生成的 HTML 上再做处理 直接改编辑器项目源码 办法是有,修修补补也能用。但如果要追求更好的用户体验那就只能自己写一个了。 另外,我们观察到一个现象,比较成功的 UGC 社区都有自己的编辑器。因为内容源是用户创造的,编辑器是否好用会对创作过程产生很大影响,所以编辑器可以说是 UGC 社区的核心之一。 Vditor 起初的定位是 Markdown 编辑器,我们已经在 v1 中达到了业界主流水准,即分屏编辑预览。v2 中我们改变了定位:“所见即所得,支持 Markdown”,这样 Vditor 将更......
Vditor 实现 Markdown 所见即所得

Vditor 实现 Markdown 所见即所得

阶段性目标 大概在去年这个时候萌生了开发 Vditor 的念头,两个月后 Vditor 第一版完成。第一版基于 textarea 开发,该版本的主要目标是替换在用的第三方编辑器,实现 B3log 各产品线上编辑器的统一。 Vditor 的定位是“下一代的 Markdown 编辑器,为未来而构建”。第一版离这个目标相差还远,但经过这大半年的迭代我们离目标已经越来越近了: 由基于 textarea 改为基于 contenteditable 实现 Markdown 引擎 Lute,并由 markdown-it 切换为 Lute 图表渲染、多媒体播放、语音阅读等功能 所见即所得模式 所见即所得 所见即所得(What you see is what you get,缩写即 WYSIWYG)在编辑器领域指的是看到啥就是啥,比如我想加粗某个字,那“加粗”只是个操作,操作结果就是得到加粗后的文本,而加粗的操作(标记符)是不会出现在文本中的。 目前看来,大部分人习惯所见即所得的编辑方式,因为这种方式非常直观。但在程序员圈子里却恰恰相反,我们更喜欢标记类排版编辑方式,其中最为流行的是 Markdown......
创业的第二个秋天

创业的第二个秋天

如果你是第一次看这个系列的文章,可从第一篇《创业的第一个春天》1 看起。 帮助别人成功,自己才有成功的机会。 Vditor & Lute 这个秋天开始实现 Vditor 的“所见即所得”特性,即实现 Typora2 那样写 Markdown 时不用分屏预览,而是边写边渲染。不过 Markdown 本身的设计用意就是“所见即所得”,所以把这个特性叫做“实时渲染”更恰当一些。这个月应该能实现完,下个月正式发布。 技术方案上我们是通过让 Lute 支持渲染“Vditor DOM”来实现。这个方案最难的部分莫过于实现源码映射3,即渲染结果字符和 Markdown 原文字符的位置对应关系。只有实现了源码映射才能实现编辑(光标)位置的正确移动。完整方案和技术细节后续会单独写一篇文档来介绍,希望能给其他在做 Markdown 编辑器的开发者带来帮助。 Latke Latke 是我们 Java 产品线的开发框架,之前的 Latke 是基于 Servlet 实现的,虽然能用但是一直不太满意: Servlet 有点重,很多特性用不上 性能不足够好 不同的 Servlet 容器有不同的部署细节,......
Lute 实现后记

Lute 实现后记

CommonMark 规范实现难点 Markdown 解析没有“报错”一说:无论是词法分析还是语法分析阶段,都不可能出现 error 退出。换句话说,对于编程语言而已,其在设计时就是准结构化的,甚至为了解析方便而特设语法(比如 golang 数据类型后置、模板中的表达式使用前缀表达式等),而 CM 则是为兼容各种写法、将各种写法规范化而设计的,所以规则非常多,目的是为了兼容而不是报错。 整体解析方面比较难的是计算缩进宽度。缩进在 Markdown 中主要用于缩进代码块、列表项对齐。因为列表项作为块容器是可以容纳任何元素的(包括块级元素和行级元素),所以当列表项出现子块时缩进就要根据列表的定义来计算。比如要考虑列表标记宽度,标记后到第一个非空字符的空格数等。最麻烦的是块引用嵌套列表的场景,因为块引用也是一种块容器。 除了缩进,还有个比较难的是“延续”判断。比如换行以后需要判断是否需要延续之前的行所在的节点。不同节点类型有不同的打断规则,最麻烦的也还是列表项和块引用,因为它们是块容器,可能出现的状态有点多。 最后还有个难点是判断列表是紧凑模式还是松散模式。因为列表是块级容器,所以需要考虑嵌....
浏览次数: 3446695
文章总数: 412
当前访客:12
RSS
翻译
开始使用