@ai-matrx/canvas

中文文档
0.10.91发布于 2026/10/28MITTypeScriptESM + CJS近 30 天下载 6.6万

AI Matrx画布:一个对接的右侧列,有分页板,分页板,全屏,等式注册,Redux相容状态和持久性.

中文使用文档
文档目录17 节
  1. 一个
  2. 尸体从不下山
  3. 了解其形状的内容
  4. 标签, 新建标签并返回
  5. 一个面板菜单
  6. 一页自己的发射器条目
  7. 打开新项目的地方
  8. 窗帘头内的控制器, "这里没有帆布"
  9. 标题工具图标
  10. 动议
  11. 标题下的一个工具栏行
  12. 工具栏图标:切换或聚焦一类
  13. 面板的色调本身为主机框架
  14. 马可斯的表情
  15. 布局规则
  16. 电子
  17. 网络弹出

@ai- matrx/ 扫描

一个对接的右侧帆布列有分页,分页,分页,扩展,以身份为基础的分页,坚持布局和ONE平板铬. 核心状态是框架独立的(它运行在电子主过程); 反应组件飞船通过 ./react。 。 。 .

import "@ai-matrx/canvas/styles.css";
import "@ai-matrx/canvas/tokens.css"; // follows a design-system theme; else built-in light/dark values
import { CanvasProvider, CanvasFrame, CanvasToggle, registerCanvasKind, defineCanvasKind } from "@ai-matrx/canvas/react";

registerCanvasKind(defineCanvasKind({ id: "note", label: "Note", icon: NoteIcon, load: () => import("./NoteView") }));

<CanvasProvider>
  <CanvasFrame><App header={<CanvasToggle />} /></CanvasFrame>
</CanvasProvider>

NoteIcon, (中文(简体) ), NoteView 财务报告和审定财务报表 App 是您的主机组件 。 Redux 主机通行证 store={bindCanvasToReduxStore(store, (s) => s.canvasHost)} 挂起后 canvasReducer。主机拥有身份、导航、放置和符号值;软件包拥有布局、光栅/塔布行为、铬和还原。 项目数据必须是简单的JSON;失败通过报告 onError。 。 。 .

条目:核心(.),反应(./react)),结构CSS(./styles.css)和默认主题值(./tokens.css) (中文(简体) ).

一个

defineCanvasKind<{ sessionKey: string }>({
  id: "terminal",
  label: "Terminal",
  icon: TerminalIcon,
  load: () => import("./TerminalView"),
  restore: "session",          // closing detaches; a restore or a move reattaches
  onAttach: ({ sessionKey, reason }) => attach(sessionKey, reason),
  onDetach: ({ sessionKey }) => detach(sessionKey), // never kill here
  HeaderLeading: NewTerminalButton, // "+ new terminal", a browser bar…
  HeaderAction: ClearButton,
  unavailable: () => (backendUp() ? null : "Terminal · next release"),
});

body: "native" 使身体成为主机接收到的框架(下方)的占位符.

尸体从不下山

每个开口物品的躯体一次被制成, 在柱子的体层中, 用测量的矩形把它放在它的面板上. 切换分页会隐藏它;拆分,将分页之间移动分页,拖放宽度,扩展并放出画布只可重置或隐藏. 反应状态,焦点和卷轴在除关闭分页之外的一切幸存. 身体在第一次显示时就挂起. isVisible 告诉它是否在屏幕上(在假投票时暂停投票); unmountWhenHidden: true 选择一个真正的重体出.

了解其形状的内容

function GraphView({ presentation }: CanvasKindProps) {   // or useCanvasPresentation() anywhere inside the body
  return <Graph direction={preferredFlowDirection(presentation) === "vertical" ? "TB" : "LR"} />;
}

presentation = { host: "canvas", width, height, orientation: "portrait" | "landscape" | "square", isNarrow, isFullscreen, paneCount }现场直播. useCanvasPresentation() 返回到任何嵌入组件中 null 在画布外 当您拥有大量垂直空间和小水平空间时, 调整: 设置图表, 时间表和卡片行从上到下, 堆放并列, 缩短工具栏 .

标签, 新建标签并返回

每个平面头有"+"(一个发射器标签). 在发射装置中选择标签的NAVIGATES,所以信头提供回. 做同样从一种:

canvas.navigate(item.id, { kind: "doc", key: docId, data: { docId } }); // records history; Back returns
canvas.back(item.id);
canvas.forward(item.id); // after Back: the same live body Back left

导航到其它地方已经打开的东西将它聚焦在那里(一个启动器标签关闭;任何其他标签保存它显示的内容). 历史是简单的JSON并持续存在。 作为标题显示的单独标签保持了自己的接近.

后从不舍一身而去. 最新的 CANVAS_KEEP_ALIVE_DEPTH (5) 退后可以达到,5个最新内容退后item.kept把尸体挂起来藏起来 后回 SAME活体;选择内容后左(从发射台再快速聊天)会使其活体复活——打出草稿仍然在那里. 旧的台阶仍然有效,但新上架。 关闭标签处理所有保存的尸体.

前进. 后页头也显示前进( A)selectCanvasCanGoForward)它返回同样的活体后向左. 航向新的地方 结束了前进的道路。 快捷键数据 canvas.back 编号 : canvas.forward (qq[/⌘])是用于桌面主机;浏览器拥有这些密钥.

条子 (克罗梅的模型). 标签平均分享条状物——每块有12个雷姆与房间——并缩到一可读地上,图标+~10个字母+"......"(.)CANVAS_TAB_MIN_WIDTH, 120px);切口名称总是以椭圆结尾,每个标签的工具提示是其全名. 在地板上, 条条只绘制符合 WALE 的标签, 运行到当前标签的周围, 尽可能少移动( WHLE) fitCanvasTabWindow),而"All tabs (N)"列出每个标签,并显示有多少个出条-一个标签从不从边缘切入(JetBrains的单行标签;一个滚动条,如VS代码或Firefox中,在两个边缘都留下了半个标签). 箭头键走每个分页。 标签从不收缩到图标 sliver , 标题中没有任何内容与另一个控件相重叠 .

分出诸相. 分页画布将 One 窗口宽的标签条保留在窗帘上方(CanvasWindowTabs),从不为每个平面画出一幅:每个平面的分页都在其中,平面的可见分页被画出为"ONE会合分页"(焦点分页有一半最强; planCanvasTabStrip),而其"+"在焦相窗格中打开. 每个面板头保留着自己的后向/前向,标题,类型控制和菜单. 侧向分拆,其平面比 paneMinWidth (网络: CANVAS_PANE_MIN_WIDTH, 280px)使堆放在同一条条下;树不变. 每个窗帘都保留着 paneMinHeight (单位:千美元)CANVAS_PANE_MIN_HEIGHT, 200px): 拆分时不会缩短子女(fitCanvasSplitSizes),且新的分块不能让两个相片最小值进入到另一个相片轴上,或者新的分页加入焦点相片 (fitCanvasSplit; 窗格菜单只提供适合的分块).

电话 (单位:千美元)CANVAS_PHONE_MAX_WIDTH, 767px): 栏中呈现出 ONE 面板——当它持有标签时的焦点, 否则是第一个( )selectCanvasPresentedPaneId) ——其条带了每一个被子的分页;从另一个被子的分页中选择一个被子呈现出被子的分页. 窗格菜单不提供分块。 布局树没有被破坏,所以一个更宽的屏幕会得到它的分出回.

一个面板菜单

窗帘有一个菜单: menuItems,然后身体提供现在, 然后东道主的 itemActions,然后拆分/弹出/关闭。 一个机构会增加自己的动作——从头里没有第二个外溢按钮:

useCanvasMenuItems(item.id, [{ id: "next", label: "Next record", onSelect: goNext }]);

一种类型的 HeaderAction 坐在坚固的窗格页眉上:绘制自带目标按钮 TapTargetButtonTransparent (只有玻璃会浮出水面).

一页自己的发射器条目

useCanvasLauncherEntry({ kind: "documents", key: conversationId, data: { conversationId }, title: "This chat's documents" });

在调用组件挂载时先显示("在本页").

打开新项目的地方

defineCanvasKind({ id: "notifications", preferredTarget: "split-down", /* … */ });

虽然画布已经显示一些东西, 这样的新项目会在下面打开( 版式规则的分出份额, 70/ 30) 或旁边; 一个已经显示它取入的面板 。 明确 target 赢了. open 财务报告和审定财务报表 toggleKind 敬礼.

窗帘头内的控制器, "这里没有帆布"

一种类型的 HeaderAction 可能会是 KindHeaderSlot;然后其正文将控件画入标题中 <KindHeaderPortal itemId={item.id}> (文字保留; useKindPaneHeaderWidth 报告页眉宽度. 将一个浸入的取景器包入 <CanvasUnavailableBoundary>内: useCanvasIsPresented() 是虚假的.

标题工具图标

<CanvasKindIcons kinds={["terminal", "review", { kind: "browser", label: "Browser" }]} variant="glass" />

每个图标切换或聚焦其类型,并在项目在前面时被按下.

动议

列动画显示其宽度 --mxc-motion-duration 编号 : --mxc-motion-ease (tokens.css: 600ms,(英语:, cubic-bezier(0.4, 0, 0.2, 1)),其内容保持了宽度,使其滑出. 一个在它旁边的回流主机在同一对上以锁步移动:

.app-shell { right: var(--canvas-w); transition: right var(--mxc-motion-duration) var(--mxc-motion-ease); }
:root[data-mxc-resizing] .app-shell { transition: none; }               /* the edge is being dragged */
@media (prefers-reduced-motion: reduce) { .app-shell { transition: none; } }

<CanvasFrame> 不需要 : 它的主要区域是柔韧的兄弟姐妹, 并遵循每个边框的列.

标题下的一个工具栏行

一种类型的 Toolbar 在窗格头和正文之间为活动者制作 项(浏览器栏)。 它是DOM, 并且一个原生的身体的框被测量到下面.

工具栏图标:切换或聚焦一类

const terminal = useCanvasKindToggle({ kind: "terminal" });
<button aria-pressed={terminal.isVisible} onClick={terminal.toggle}>…</button>
// outside React (Electron main, a menu handler):
toggleKind(controller, { kind: "terminal" }); // absent → open · behind → focus · in front → close

whenVisible: "hide" 将画布收起,而不是关闭标签.

面板的色调本身为主机框架

CanvasPanel (标题或 header, (中文(简体) ), leading, (中文(简体) ), actions, (中文(简体) ), renderMenu, (中文(简体) ), onToggleExpand, (中文(简体) ), onClose, (中文(简体) ), CanvasPanelEmpty, (中文(简体) ), CanvasHeaderButton, (中文(简体) ), CanvasTab, (中文(简体) ), BrowserTabs, (中文(简体) ), BrowserBar – – – – – – – – – – – – – – – – – – – – – – – , – – – – – – – – – , – – , – , – , – , – , – – , – , – , – , – – , – , – , – , – , – , – , – – , – , – , – , , – , – , – , , – , , – , – , – , – , – , , – – , – , – , – , , , , – , – – – , , , – , , – , , , – 它们也会在供货商之外产生.

马可斯的表情

<CanvasProvider look="mac"> 将每个窗格画为Mac 窗口的一段而不是卡片: 柱子冲到一个发线后面的窗边,被发线分割的窗子,每个窗子 标出统一工具栏的带头( u)--mxc-toolbar-h,默认52px) — 活动标签的标题居中 以粗体表示,其类型的图标为代理图标(Terminal, Finder),或者该类型的图标 Toolbar 改为: (Safari的地址栏). “ +” 与跟踪控件同坐; 两个或多个标签得到 macOS 标签栏 在乐队下。 将信头置于自己的窗口按钮设置下的主机 --mxc-header-inset-start 在专栏。 默认 look="card" 保留 Claude 桌面/ 代码卡.

布局规则

<CanvasProvider layoutRules={CANVAS_DESKTOP_LAYOUT_RULES} regionWidth={windowWidth - railAndSidebar}>
规则Web( 默认)桌面
defaultWidth640号{ fraction: 0.36 }
minWidth360 (中文(简体) )360 (中文(简体) )
centreMinWidth (画布先收缩)600个第520条
stackSplit (分出: 第一张平板电脑份额)0.7 (简体中文)0.7 (简体中文)
sideSplit0.5 (韩语)0.5 (韩语)
paneMinWidth (更窄相接的平板叠)280号360 (中文(简体) )
paneMinHeight (没有更短的窗帘;无法匹配的分页或打开标签)200个200个

扩展为每个窗口一面旗;它会填充主机所设置的列(useCanvasColumnWidth() 返回时 null) (中文(简体) ).

电子

主进程为一商店;每个窗口的渲染器都是客户端.

// main
import { createCanvasStore, createCanvasController, createKeyValueCanvasPersistence, isCanvasAction,
  CANVAS_DESKTOP_LAYOUT_RULES, CANVAS_SHORTCUTS, runCanvasCommand } from "@ai-matrx/canvas";

const store = createCanvasStore();
const canvas = createCanvasController({
  store,
  persistence: createKeyValueCanvasPersistence({ get: (k) => kv.get(k), set: (k, v) => kv.set(k, v) }),
  layoutRules: CANVAS_DESKTOP_LAYOUT_RULES,
  isRestorable: (kind) => kind !== "browser", // main has no kind registry: name the kinds that never come back
});
canvas.start(); // hydrates, then autosaves

ipcMain.handle("canvas:get", () => store.getState());
ipcMain.handle("canvas:dispatch", (_e, action: unknown) => { if (isCanvasAction(action)) store.dispatch(action); });
store.subscribe(() => { for (const w of BrowserWindow.getAllWindows()) w.webContents.send("canvas:snapshot", store.getState()); });

// Shortcuts as data: bind them where keys inside native views still arrive.
for (const s of CANVAS_SHORTCUTS) { /* menu accelerator or before-input-event → */ runCanvasCommand(canvas.forWindow(windowIdOf(win)), s.id); }
// A BrowserWindow that closes: its tabs go home.
win.on("closed", () => canvas.closeWindow(windowIdOf(win)));
// renderer (one per window)
import { createRemoteCanvasStore } from "@ai-matrx/canvas";

const store = createRemoteCanvasStore({
  initialSnapshot: await ipcRenderer.invoke("canvas:get"),
  dispatch: (action) => ipcRenderer.invoke("canvas:dispatch", action),
  subscribe: (onSnapshot) => {
    const on = (_e: unknown, s: CanvasState) => onSnapshot(s);
    ipcRenderer.on("canvas:snapshot", on);
    return () => ipcRenderer.off("canvas:snapshot", on);
  },
});

<CanvasProvider
  store={store}                    // remote: the main process hydrates and saves
  windowId={myWindowId}            // "main" for the first window
  layoutRules={CANVAS_DESKTOP_LAYOUT_RULES}
  regionWidth={rightOfSidebar}
  hotkeys={false}                  // main binds CANVAS_SHORTCUTS
  onNativeBodyFrame={(itemId, rect, { visible }) => ipc.send("view:place", itemId, visible ? rect : null)}
  showMenu={(entries, anchor) => ipc.invoke("menu:popup", entries, anchor)}   // Menu.popup → chosen id
  overlay={{ show: (el) => { ipc.send("overlay:show", el); return () => ipc.send("overlay:hide", el); } }}
  popOut={(item) => ipc.invoke("canvas:pop-out", item.id)}                    // → { windowId } of a new BrowserWindow
>
  <CanvasColumn />  {/* a pop-out window renders <CanvasColumn placement="fill" /> */}
</CanvasProvider>

矩形是窗口 CSS 像素,带有 devicePixelRatio,框中报告布局已改变(放电效果+调整观察员大小)。 打电话 useCanvasNativeBodies().remeasure() 每个动画框,而主机动画则自行布局. visible: false 指包 DOM 在视图上浮起(一个 DOM 菜单或标签拖放):隐藏它,从而看到并接收了 DOM .

网络弹出

popOut={async (item) => { const id = `pop-${item.id}`; openFloatingPanel(id); return { windowId: id }; }}
// the floating panel:
<CanvasWindowScope windowId={id}><CanvasColumn placement="fill" /></CanvasWindowScope>

当弹出窗口消失时 state.windows 关闭面板.

麻省理工学院.

版本与发布记录

@ai-matrx/canvas 共发布 120 个版本,首次发布于 2026/10/02;过去 12 个月发布了 120 个版本。

版本发布日期距今
0.10.91latest2026/10/289 天前
0.10.902026/10/2611 天前
0.10.892026/10/2511 天前
0.10.882026/10/2512 天前
0.10.872026/10/2412 天前
0.10.862026/10/2413 天前

其他发布通道:bootstrap → 0.0.0-bootstrap.0。

关于 @ai-matrx/canvas 的常见问题

@ai-matrx/canvas 是什么?

AI Matrx画布:一个对接的右侧列,有分页板,分页板,全屏,等式注册,Redux相容状态和持久性.

如何安装 @ai-matrx/canvas?

在项目目录运行 npm install @ai-matrx/canvas,或使用 pnpm add / yarn add / bun add。

@ai-matrx/canvas 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@ai-matrx/canvas 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@ai-matrx/canvas 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@ai-matrx/canvas 可以商用吗?

@ai-matrx/canvas 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@ai-matrx/canvas 的下载量有多少?

根据 npm 官方下载统计,@ai-matrx/canvas 在 2026/10/05 – 2026/11/03 这 30 天内被下载 65,606 次,日均约 2,187 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@ai-matrx/canvas 还在维护吗?

最新版本 0.10.91 发布于 2026/10/28(9 天前);至今共发布 120 个版本,其中过去 12 个月发布了 120 个;首次发布于 2026/10/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。