数独网站有两个通病:满屏广告,以及只给鼠标用的交互。我想安静思考,不想跟浏览器较劲。
于是我自己做了一个。叫 Kodiak Sudoku——免费、无广告、键盘优先,带 Vim 快捷键,排行榜不作假。以下是我的搭建过程。
![]()
技术栈
- Next.js 15(App Router)
- React 19
- 全 TypeScript
- Supabase 管数据库(认证、排行榜、每日谜题状态)
- Drizzle ORM 架在 Supabase 上
- Tailwind CSS
- Vercel 托管 + Edge Config 做功能开关
没什么 exotic 的东西,就是一套能让我快速开发、又能放心上线的组合。
我只在乎三件事
1. 免费,无广告
听起来简单,但这决定了每一个技术决策。不用加载广告 SDK,没有布局偏移,没有第三方追踪。页面快,是因为没什么东西拖慢它。目前托管费我自掏腰包——Vercel 和 Supabase 的免费额度,对这种规模的小项目来说相当够用。
2. 键盘优先,带 Vim 快捷键
大多数数独站为鼠标/触摸设计。手机上点格子没问题,但桌面端太慢。我想像操作其他工具一样操作棋盘——hjkl、方向键、数字键直接填数。
输入处理成了整个项目里最有意思的部分之一。React 的合成事件系统和浏览器原生焦点行为并不总合拍,尤其是当你想让一个网格用起来像文本编辑器的时候。让 Vim 式导航足够跟手、又不跟 DOM 打架,迭代了好几轮。
如果你也折腾过这类东西——键盘驱动 UI、焦点管理、无障碍——想听听你怎么解决的。
3. 诚实的排行榜
其他数独排行榜让我烦的一点是:用了提示的成绩和纯手打的成绩混在一起。用了三次提示、4 分钟完成的人,排在纯手打 5 分钟的人前面,这没道理。
Kodiak 把两者分开。纯手打和提示辅助各自独立排名。小事一桩,但排行榜的感觉完全变了——这是你能为之骄傲的东西,而不只是一个数字。
几个技术细节
边缘的每日谜题状态
每日谜题对所有人相同。我用 Vercel Edge Config 存储和分发当前谜题,数据全球分布、基本零延迟。用户级的状态(进度、计时、提示使用)存在 Supabase。
Drizzle + Supabase
之前用过 Prisma,这次想试试 Drizzle。类型推断很好,查询构建器更像写 SQL,我喜欢。跟 Supabase 集成很顺——Supabase 管认证和 RLS 策略,Drizzle 负责查询层。
谜题生成 vs. 人工筛选
我最终选择了人工筛选谜题,而非实时生成。自动生成的谜题往往难度不均,或者解法无趣。我建了一套工具链来批量生成、评分、测试,然后手动挑出一批质量稳定的入库。
这意味着每日谜题是提前准备好的,不是当天算出来的。权衡之下,体验更可控。
键盘事件的坑
最耗时的 bug 都跟焦点有关。比如按 Tab 切出棋盘再切回来,焦点落在哪?数字键该填数字还是触发浏览器搜索?hjkl 滚动页面怎么办?
我的解法:棋盘是一个 role="grid" 的容器,每个格子是 role="gridcell"。用 useRef 跟踪当前焦点坐标,手动管理 tabindex。事件拦截在 document 层,判断目标后再决定是否 preventDefault。
代码不算优雅,但行为可预测。
为什么没上 PWA
考虑过,但暂时没做。数独是"打开即玩"的场景,不需要离线优先。加上 PWA 的缓存策略会增加复杂度,而我想保持部署简单。如果以后有需求,Edge Config 的架构也方便扩展。
下一步
- 主题切换(深色模式已支持,想加更多)
- 复盘模式:完成后回放操作序列
- 可选的"硬核模式":禁用候选数标记
但核心体验已经是我想要的:打开网站,按任意键开始,不用等广告,不用摸鼠标。
如果你也玩数独,试试 kodiaksudoku.com。有反馈直接发我,或者开 GitHub issue。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.