ESC
开源 2 分钟阅读

shapeshift:一个能读懂你意图的输入框——随着输入自动变形为合适的 UI,由 TypeSafe Jev 驱动,支持完全离线

开源项目 shapeshift 推出新型输入交互:单一文本框可根据输入内容实时变形为事件卡片、清单、计时器、取色器、账单分摊等约 19 种 UI 组件。意图识别由 TypeSafe AI 的 Jev 模型一次调用并行回答 14 个类型化问题,日期、金额、单位换算等由确定性代码处理,默认完全离线可用、无需账号。项目基于 Next.js、React 19 与 TypeScript 构建,采用 MIT

来源:GitHub

Shapeshift

一个能读懂你意图的输入框。 一个文本框,随着你的输入自动变形为合适的 UI——事件卡片、待办清单、计时器、取色器、账单分摊、投票、单位换算等等。

输入 'dinner with priya friday 8pm on zoom' 后,文本框变形为事件卡片,再变形为购物清单
在线体验 · 观看完整 60 秒演示(1080p60)

dinner with priya friday 8pm on zoom   →  Event card · Friday · 8 PM · Priya · Video call
buy milk, eggs, bread and coffee       →  Shopping checklist
split 2400 between 3                   →  ₹800 each
minecraft diamond                      →  #4AEDD9

意图由 TypeSafe AI 的 Jev 模型进行分类:一次调用并行回答 14 个类型化问题(是哪种卡片,外加"是否视频通话?““是否紧急?“等信号)。其余一切——日期、金额、单位、数学——都是确定性代码。Jev 负责决策,代码负责计算。

一次 Jev 调用并行回答 14 个问题;确定性解析器读取同一文本提取数值

它默认完全离线工作,内置关键词分类器,无需账号即可运行。

快速开始

需要 Bun 1.2+。

bun install
bun dev

打开 http://localhost:3000 开始输入。按 / 查看所有卡片类型。

使用在线 Jev 模型(可选)

cp .env.example .env.local
# 然后设置 TYPESAFE_API_KEY=...(在 https://console.typesafe.ai/keys 获取)

重启 bun dev。右下角的延迟读数会从 jev-offline 切换为 jev-1.13.0。密钥只在服务器端(/api/intent)读取,永远不会到达浏览器。如果 API 不可达或被限流,Shapeshift 会静默回退到离线模式。

变量默认值作用
TYPESAFE_API_KEY(空)启用在线模型。留空或为占位值时保持离线。
JEV_MODELjev-1.13.0固定的模型版本。
NEXT_PUBLIC_USE_MOCKfalse设为 true 则即使有密钥也强制离线。
NEXT_PUBLIC_SITE_URLhttp://localhost:3000用于 Open Graph 元数据。

卡片类型

卡片试试输入
事件lunch with rahul and anna tomorrow
提醒remind me to pay rent tomorrow urgent
清单buy milk, eggs, bread and coffee
计时器25 min focus
习惯打卡gym 3x a week
颜色#ff6b35、tiffany blue、minecraft diamond
分摊split 2400 between 3
支出spent 450 on uber
换算5 miles in km、72f to c
计算18% of 3450
行程flight to goa next weekend
投票pizza or burgers for friday?
联系人rahul 98200 12345 rahul@mail.com
书签https://vercel.com/blog check later
倒计时days until christmas
时区3pm pst in ist、what time is it in tokyo
随机roll 2d6、flip a coin、pick one: tacos, sushi or pizza
目标read 12 books this year, 4 done
笔记其他任何内容

保存的卡片存放在你的浏览器(localStorage)中,直到你删除它们。点击卡片即可编辑。

键盘操作

按键操作
Enter保存卡片
Esc清空(或取消编辑)
Tab保留淡淡的预览
← →在"你是想输入"候选间切换
/打开所有卡片类型

URL 参数:?debug=1 显示所有概率;?demo=1&loop=1 播放脚本化演示。

工作原理

按键、防抖 Hook、服务端路由、Jev 或离线分类器、决策、信号门控、解析、卡片

原始模型输出会随输入不断闪烁,因此一个小型状态机将置信度转化为平稳的 UI 状态。只有当挑战者连续两次胜出(或非常有把握)时,卡片才会切换,信号徽章则使用开/关滞回区间。

状态:输入、幽灵预览、二选一候选、已确定的卡片,以及它们之间的阈值

图表均为 Excalidraw 文件——在 excalidraw.com 打开任意 docs/diagrams/*.excalidraw 即可编辑。

路径内容
src/components/intents/registry.ts扩展点。 每种卡片类型一个条目。
src/lib/jev/questions.tsJev 问题 schema
src/lib/jev/mock.ts离线关键词分类器(输出结构相同)
src/lib/parse/每种卡片类型一个确定性解析器
src/lib/decide.ts、src/lib/signals.ts平稳 UI 状态机
src/components/shapeshift/外壳、候选 chips、调色板、已保存列表、HUD

添加一种卡片类型

  1. 在 src/lib/jev/types.ts 的 INTENT_KEYS 中添加 key。
  2. 在 src/lib/jev/questions.ts 的 intent 中添加互不重叠的判据。
  3. 在 src/lib/parse/ 中编写解析器,并在 src/lib/parse/index.ts 中注册。
  4. 在 src/components/intents/ 中编写卡片组件,并添加 registry 条目。
  5. 教会 src/lib/jev/mock.ts 中的离线分类器,并添加测试。

TypeScript 会指出你遗漏的任何内容。

开发

bun run check    # 类型检查 + lint + 测试
bun test         # 解析器、决策、信号与分类器测试
bun run build

技术栈:Next.js (App Router) · React 19 · TypeScript (strict) · Tailwind CSS v4 · shadcn/ui · Motion · chrono-node · zod。

许可证

MIT