首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >我开源的 AI + Excalidraw 画图更新啦!

我开源的 AI + Excalidraw 画图更新啦!

作者头像
co松柏
发布2026-04-09 12:49:10
发布2026-04-09 12:49:10
1770
举报

✨前言

Hi,我是松柏。

前几天我做了个开源的 AI + Excalidraw 的绘图工具,收到了很多小伙伴的好评:

短短几天 Github https://github.com/co-pine/ai-excalidraw 也 100+ star ⭐️ 了:

所以今天我又花了一些时间对这个工具进行了更新,主要增加了多会话管理选中元素更新、删除元素这些功能,让体验更加流畅!先一起看看效果吧:

选中元素更新:

删除元素:

多会话管理,也就是说,每个对话的画布是相互隔离的,互不影响:

体验地址:https://www.lzkz.top/tool/excalidraw

Github 地址:https://github.com/co-pine/ai-excalidraw

废话不多说,关注点赞,我们看看这次的更新及其实现思路吧!

🎯核心更新

一、多会话管理

之前的版本所有会话共享一张画布,很容易互相污染,不好管理。所以这次更新支持了多会话管理,可以同时维护多个独立的画布和对话历史。

实现思路很简单,为每个会话生成唯一的 sessionId,然后在 localStorage 中分别存储:

代码语言:javascript
复制
// 会话数据结构
interface Session {
  id: string
  name: string
  createdAt: number
  messages: Message[]
  elements: ExcalidrawElement[]
}

// 保存会话
const saveSession = (session: Session) => {
  localStorage.setItem(`session-${session.id}`, JSON.stringify(session))
}

// 切换会话
const switchSession = (sessionId: string) => {
  const session = JSON.parse(localStorage.getItem(`session-${sessionId}`))
  // 恢复对话历史和画布元素
  setMessages(session.messages)
  excalidrawAPI.updateScene({ elements: session.elements })
}

这样每个会话都是完全独立的,可以随时创建新会话或切换到之前的会话继续编辑,非常方便。所有数据依然是存储在本地,不用担心安全问题:

二、选中元素更新

点击选中元素后,对话框的上方会显示元素的信息,在发消息时会把元素信息也一起发送给 AI,这样 AI 就能基于画布的状态实现精确修改,“指哪改哪”。

这个功能的核心实现是通过 Excalidraw API 获取选中元素的信息:

代码语言:javascript
复制
// 获取当前选中的元素
const getSelectedElements = () => {
  const api = excalidrawAPIRef.current
  const appState = api.getAppState()
  const allElements = api.getSceneElements()

  // 过滤出选中的元素
  return allElements.filter(el => 
    appState.selectedElementIds[el.id]
  )
}

// 更新选中的元素
const updateSelectedElements = (updates: Partial<ExcalidrawElement>) => {
  const selectedElements = getSelectedElements()
  const updatedElements = selectedElements.map(el => ({
    ...el,
    ...updates,
  }))

  // 应用更新
  api.updateScene({
    elements: allElements.map(el => 
      updatedElements.find(updated => updated.id === el.id) || el
    )
  })
}

这个功能给我的体验非常好,比如我可以直接选中不满意的多个节点,然后描述修改需求,就能进行精确的局部修改了。

三、AI 工具调用(Function Calling)

为了让这个工具更好用,我还引入了 AI 工具调用能力,让 AI 可以主动获取画布信息和操作元素。

这里主要提供了两个核心工具:

1)获取元素信息(get_elements)

AI 可以主动查询画布上的元素信息,比如:

  • 用户:"帮我看看画布上有什么"
  • AI:调用 get_elements() 获取所有元素
  • AI 回复:"画布上有 3 个矩形、2 个箭头和 5 个文本元素..."
代码语言:javascript
复制
const tools = {
  get_elements: {
    description: '获取画布上的所有元素信息',
    parameters: {
      type: 'object',
      properties: {
        elementIds: {
          type: 'array',
          description: '可选,指定要获取的元素 ID 列表'
        }
      }
    },
    execute: (args) => {
      const api = excalidrawAPIRef.current
      const elements = api.getSceneElements()

      if (args.elementIds) {
        return elements.filter(el => args.elementIds.includes(el.id))
      }
      return elements
    }
  }
}
2)删除元素(delete_elements)

AI 还可以帮你删除不需要的元素:

  • 用户:"把所有红色的矩形都删掉"
  • AI:调用 get_elements() 找到所有红色矩形
  • AI:调用 delete_elements(ids) 删除它们
代码语言:javascript
复制
const tools = {
  delete_elements: {
    description: '删除指定的元素',
    parameters: {
      type: 'object',
      properties: {
        elementIds: {
          type: 'array',
          description: '要删除的元素 ID 列表',
          items: { type: 'string' }
        }
      },
      required: ['elementIds']
    },
    execute: (args) => {
      const api = excalidrawAPIRef.current
      const elements = api.getSceneElements()

      // 标记为删除
      const updatedElements = elements.map(el => 
        args.elementIds.includes(el.id) 
          ? { ...el, isDeleted: true }
          : el
      )

      api.updateScene({ elements: updatedElements })
    }
  }
}

我觉得工具调用还是挺方便的,可以很大程度上提高使用的便捷性。

四、其他

1)因为现在很多模型都是思考模型,而且思考的比较久,所以为了让用户体验更好一点,这次更新会把模型的思考过程展示给用户,减少纯等待的时间。

实现上很简单,就是在 AI 返回的流式响应中,单独处理 reasoning_content 字段:

代码语言:javascript
复制
const streamAI = async (prompt: string) => {
  const response = await fetch('/api/chat', {
    method: 'POST',
    body: JSON.stringify({ messages, tools })
  })

  const reader = response.body.getReader()
  let reasoning = ''
  let content = ''

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    const text = new TextDecoder().decode(value)

    // 解析流式数据
    if (text.includes('reasoning_content')) {
      reasoning += extractReasoning(text)
      setThinkingContent(reasoning)  // 展示思考过程
    } else {
      content += text
      setResponse(content)  // 展示正常回复
    }
  }
}

2)这次更新没有加没有记忆功能,个人觉得没有太大必要,来回传递画布内容太费 token 了,而且过长的上下文也会影响到 AI 输出的质量。所以从成本和体验上考虑,我选择了不加记忆功能,让 AI 通过工具调用来了解画布状态。

3)上期有一些小伙伴说希望能出对应的 Obsidian 插件,但是我本人很少用 Obsidian,不清楚大家希望这个插件做成什么样、如何交互等等,所以这个就没做,不过也欢迎其他感兴趣的小伙伴在我代码的基础上改造。

🎯结语

以上就是 AI Excalidraw 这个项目的本次更新啦,项目已经开源,欢迎大家点个 Star ⭐、提 issue 和 pr 。开源和体验地址:

  • 🔗 GitHub:https://github.com/co-pine/ai-excalidraw
  • 🌐 在线体验:https://www.lzkz.top/tool/excalidraw

如果觉得有用,欢迎关注转发点赞呀,也可以加我微信 co_pine 一起学习交流,下期再见,拜拜👋🏻。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 co松柏 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ✨前言
  • 🎯核心更新
    • 一、多会话管理
    • 二、选中元素更新
    • 三、AI 工具调用(Function Calling)
      • 1)获取元素信息(get_elements)
      • 2)删除元素(delete_elements)
    • 四、其他
  • 🎯结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档