@ kortix/ui
的共享React组件库 apps/web,Kortix应用程序,
白标演示和SDK主机。组件是顺风v4类字符串主题
CSS变量。整个会话UI住在这里,每个模块
职责(如下布局)。 apps/web 像任何主机一样导入它:模块
条目、原语和深度路径的简短列表 (如下)。
使用@kortix/ui构建: 中的三个白色标签示例 apps/white-label/
(自述):
Lumen工作区,将Kortix工作空间置于其自身之下
主题,其中每个屏幕都是这个包的一个模块;
团队控制台,内部工具;和
流明,具有自己的堆栈的合法AI SaaS。

安装: npm i @kortix/ui @kortix/sdk。两个软件包都带有Kortix版本
版本,所以安装相同的版本。模块条目(session,
workspace, theme 以及下面的其他版本)从0.14.4发布。0.14.3有根,
组件子路径和样式表。在kortix-ai/suna内部:
"@kortix/ui": "workspace:*".
react 和 react-dom 19是同行 (PowerPoint预览需要React 19.2.7或更高版本)。
- 公共文档: 的 “Kortix UI” 部分
apps/web/content/docs/developers.mdx(https://kortix.com/docs/developers#kortix-ui)。 - 代理技能:
skills/kortix-ui/SKILL.md,references/custom-dashboard.md(整个仪表盘),references/modules.md(每个条目,它的道具,一个例子)和references/theming.md(每个主题键)。Kortix会话加载相同的文件 从packages/starter/templates/managed/skills/kortix-ui/SKILL.md(kortix-ui是在KORTIX_MANAGED_SKILL_NAMES,所以沙盒烘焙会运送它)。src/skill.test.ts当副本 (SKILL.md和references/)不同, 或当技能的组件表 (根,主题) 或references/modules.md(每个模块条目)缺少一个导出。 - 实时目录:
/design-system路线apps/web.
使用它
Tailwind主机 (apps/web,带有Tailwind的Vite应用程序):
@import 'tailwindcss';
@import '@kortix/ui/tailwind.css';
安装插件 tailwind.css 负载(可选对等体),所以主机没有
Tailwind从不安装Tailwind):
npm i tw-animate-css tailwind-scrollbar tailwind-scrollbar-hide.
tailwind.css 导入 tw-animate-css, tokens.css ( :root / .dark
值), theme.css (深色变体,基本规则, @theme 钥匙,实用程序,
关键帧, @source 在包裹上) 和 tailwind-scrollbar 插件。
使用自己的令牌值的主机逐个导入这些部分。每一个
线路除外 tokens.css 需要:不需要 tw-animate-css 和插件,
叠加动画和 scrollbar-hide 退出,没有生成错误。
@import 'tailwindcss';
@import 'tw-animate-css';
@import '@kortix/ui/tokens.css'; /* optional */
@import '@kortix/ui/theme.css';
@plugin 'tailwind-scrollbar';
@plugin 'tailwind-scrollbar-hide';
tokens.css 集合 --spacing: 0.23rem美国比顺风银行紧缩8% 0.25rem.
主机自己的间距实用程序跟随它 (p-4 是14.72 px)。保持库存
顺风间距,设置 --spacing: 0.25rem 进口后;的组件
然后将8% 渲染得更大。
theme.css 包含每个主机需要的基本规则: body 绘画
--background / --foreground, * 边框默认为 --border,
color-scheme 遵循 .dark, ::selection 读取 --selection,
交互式元素获得指针游标。
不带顺风的主机: import '@kortix/ui/styles.css'. 它是
dist/styles.css,由承诺和重建 pnpm --filter @kortix/ui build:styles 在任何改变之后 src/. src/exports.test.ts 失败时
提交的文件从新构建中漂移。
仅kortix拥有的表面(品牌D8a): @import '@kortix/ui/fonts.css';
后 theme.css 加载Roobert并设置 --font-roobert /
--font-roobert-mono. 白标主机从不导入它。
主机适配器
山 UiProvider 一旦在根。每个道具都是可选的。没有主机
任何道具都有这些默认值:
| 道具 | 默认没有它 |
|---|---|
strings | 英文副本 (defaultUiStrings) |
link | 一个普通 <a>: 每个链接都是一个完整的页面加载 |
image | 一个普通 <img> |
navigation | defaultUiNavigation: push / replace 是整页加载, usePathname / useSearchParams 读 window.location, useParams 返回 {}. observesHistory: true (Next.js):会话选项卡或查询参数的UI更改被写入历史,没有导航;off (React Router),它通过 useRouter |
i18n | 捆绑的英文目录 @kortix/ui/i18n |
useViewerId | 未登录用户(undefined): 一个人会话不命名发件人,也不命名 “停止”。几个人写的一个会话需要它 |
useBrand | 科蒂克斯标志 |
config | { connectorsEnabled: true, managedModelsEnabled: false, terminalEnabled: true, cliConnectEnabled: true, publicSharesEnabled: true, commandPaletteShortcut: false, settingsShortcut: false, accountApiEnabled: true }. config.cliConnectEnabled (默认为true): false隐藏终端的“从您的机器连接”栏 <origin>/install 安装程序和 kortix sessions shell),对于没有提供安装程序且没有附带Kortix CLI的主机。 config.publicSharesEnabled (默认为true): false会隐藏所有添加或列出公共链接的控件 (共享对话框的公共链接部分,浏览器面板的复制链接和管理链接,文件和应用程序查看者的复制链接) share.public_path (/share/session/<token>)。 config.accountApiEnabled (默认为true):如果主机接收一个操作符密钥并阻止帐户路由,则为false;然后没有IAM探测,帐户成员或组列表,计费或会话监督请求运行,共享选择器不提供“特定人员”选项(项目成员是运营商的帐户),计费状态保持空,并且每个权限门显示其控制(服务器强制执行每个路由)。 这两个快捷标志表示主机使用⌘K打开命令调色板或使用⌘,;只有这样,作曲家才会暗示他们。 config.providerIconBaseUrl: 其中主机提供提供商徽标 (<base>/<icon>.svg);没有它,它们从models.dev加载,Kortix提供程序绘图 useBrand的图标。计费是SDK标志 billingEnabled 的 createKortix({ backendUrl, getToken, billingEnabled }),每个账单门都会读到。它适用于Kortix自己的应用程序: 它的升级操作会打开应用程序/web安装的Kortix计划对话框。自定义主机将其关闭,因此没有升级控件呈现。 |
components | 无插槽: 打开缺失插槽的控件被隐藏 (ShareSessionModal, HomeAnnouncement, Wallpaper, ProjectProviderModal, ProviderAccountsModal, ConnectorConnectModal) |
strings 取的任意子集 UiStrings. MermaidRenderer的控制是
只说英语。
没有路由器或i18n(纯Vite, CDN页面)的主机:
import { UiProvider } from '@kortix/ui';
<UiProvider>{app}</UiProvider>
Next.js主机传递自己的钩子。传递稳定对象 (模块级别):
import { UiProvider, type UiNavigation } from '@kortix/ui';
import type { UiI18nHost } from '@kortix/ui/i18n';
import NextImage from 'next/image';
import NextLink from 'next/link';
import { useParams, usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useLocale, useTranslations } from 'next-intl';
const navigation: UiNavigation = { useRouter, usePathname, useSearchParams, useParams, observesHistory: true };
const i18n: UiI18nHost = { useTranslations, useLocale };
const useViewerId = () => useAuth().user?.id;
<UiProvider
strings={{ confirm: t('confirm') }}
link={NextLink}
image={NextImage}
navigation={navigation}
i18n={i18n}
useViewerId={useViewerId}
config={{ managedModelsEnabled: true }}
>
{app}
</UiProvider>
image={NextImage} 需要 images.remotePatterns 为每个远程徽标URL。
会话UI读取它从中呈现的项目和会话
SessionIdsProvider,从来没有从路线params。 SessionThread,
SessionWorkspace 和 ProjectHome 他们自己把它装上,连同
SDK的项目范围(internal/project-scope.tsx). 在其他街区周围,
山 KortixProjectProvider (@kortix/sdk/react),
<SessionIdsProvider projectId={projectId} sessionId={sessionId}>.
useSessionIds() 读取它; 没有提供者,两个id都是 undefined,
需要它们的组件不渲染任何内容。根条目不从
@kortix/sdk: scripts/build.mjs 负载 dist/providers/session-ids.js 在
平原节点保持如此。
遥测是模块级的,所以简单的函数也可以报告。呼叫
setUiTelemetry 一旦模块加载; 省略的函数保持其默认值
(track, captureMessage, addBreadcrumb 什么也不做; captureException
日志到控制台):
import { setUiTelemetry } from '@kortix/ui/lib/telemetry';
declare module '@kortix/ui/lib/telemetry' {
interface UiTelemetryRegister { event: 'panel_opened' | 'panel_closed' } // optional: tsc checks event names
}
setUiTelemetry({ track: (event, properties) => analytics.capture(event, properties) });
翻译: @kortix/ui/i18n 出口 useTranslations 和 useLocale
(下一个intl签名: t, t.rich, t.markup, t.raw, t.has),
UiI18nHost, UiTranslator, UiMessages, defaultUiMessages (英文
目录,由 pnpm --filter @kortix/ui gen-i18n-catalog),
createTranslator. i18n={{ locale, messages }} 替换目录
没有主机挂钩。目录在每个进口的捆绑中发货
@kortix/ui/i18n (约121kb缩小,46kb gzip),也当主机
传递自己的钩子。
另外两个无主机助手: lazyClient(load, { fallback })
(从 @kortix/ui Root)只在客户端上加载组件
next/dynamic(..., { ssr: false }) 合同。 ErrorBoundary 和
ErrorBoundaryFallback (@kortix/ui/error-boundary) 报告给
captureException.
暗模式是 dark 课上 <html> (useIsDark() 读它)。
按子路径导入组件: import { Button } from '@kortix/ui/button'.
根 @kortix/ui 桶只持有光核心基元。敬酒需要一个
<Toaster /> 从 @kortix/ui/toast 在根源上; IconProvider
(@kortix/ui/icon-provider) 设置房屋图标权重。
路线
组件链接到的每个URL都来自一个路由表, setUiRoutes (@kortix/ui),在模块加载时调用一次。默认值是Kortix自己的路径: project /projects/<id>, session /projects/<id>/sessions/<sessionId>, sessions /projects/<id>/sessions (?view=archived|deleted), files /projects/<id>/files, drive /projects/<id>/drive, apps /projects/<id>/apps, members /projects/<id>/members, sandboxSettings /projects/<id>/config?section=sandbox, customize /projects/<id>/customize/<page> (代理,技能,连接器,触发器,提醒,模型,秘密,审查,设置), channel /projects/<id>/c/<slug> 和 dm /projects/<id>/dm/<agent> (侧边栏链接他们,而项目的 project_channels 旗子已亮起)。返回您在其他地方提供的页面的路径。返回 null 对于您不提供服务的页面: 指向它的每个链接都是隐藏的。 project, session, channel 和 dm 不能为空。
模块
Kortix会话的每个部分都是一个包含一个条目的模块。精心挑选的名字
是公共API (semver)。每一个需求 @kortix/sdk 和 @tanstack/react-query
5(一个可选的peer,与主机共享,所以两者都读一个 QueryClient: 捆绑器
没有它失败,npm不警告),一个
QueryClientProvider对于线, @openuidev/[email protected] 和 zod:
npm i @kortix/ui @kortix/sdk @tanstack/react-query @openuidev/[email protected] zod @phosphor-icons/[email protected]
(图标集,版本为 @kortix/ui 别针,你自己的图标)。
| 条目 | 它是什么? | 最小安装 |
|---|---|---|
./workspace | SessionWorkspace (螺纹+可调整大小的侧板), ProjectHome, ProjectSessionList, ProjectFilesView, ShareSessionModal | <SessionWorkspace session={useSession(p, s)} /> |
./session | SessionThread,转弯, ToolRegistry,提示,采摘者 | <SessionThread session={useSession(p, s)} /> |
./composer | ComposerChatInput 发送管道 | 在项目范围内 |
./session-panel | SessionPanelProvider, ActionPanel, SessionFiles, SessionChanges | SessionPanelProvider |
./files, ./file-viewer | 项目和沙箱文件, FilePreviewHost; FileViewer, FileContentRenderer | ProjectFilesView; <FileViewer content fileName /> |
./terminal, ./browser | SessionTerminal; SessionBrowser, BrowserLanding | 已挂载会话的运行时id |
./markdown, ./genui, ./diff, ./changes | UnifiedMarkdown,GenUI块, DiffView, ChangeList | 无 |
./apps, ./access, ./customize | 应用程序框架;分享和角色;项目配置页面 | projectId |
./theme, ./i18n, ./icons | 键入的主题; 翻译; 字形 | 无 |
import { useSession } from '@kortix/sdk/react';
import { SessionWorkspace } from '@kortix/ui/workspace';
export function Session({ projectId, sessionId }: { projectId: string; sessionId: string }) {
return <SessionWorkspace session={useSession(projectId, sessionId)} />;
}
SessionThread 单独显示文件和预览打开在一个对话框时,没有边
面板已安装。 ProjectHome 对象创建会话
提示并导航到 /projects/<id>/sessions/<sessionId>); beforeSend
盖茨。 ToolRegistry.register('<tool>', Row) 替换工具行。
在任意深度组成一个自定义工作区:您自己的列在其中
SessionWorkspace (children),单独的面板 (SessionPanelProvider +
ActionPanel),内部单个块 KortixProjectProvider +
SessionIdsProvider,或者你自己的每一部分 renderParts 从
@kortix/sdk/react.
深路没有尽头。 @kortix/ui/internal/* 和
@kortix/ui/<module>/<path> 仅适用于Kortix自己的应用程序导入的文件
(scripts/deep-exports.json). 主机导入条目和原语。
整个仪表板 (客户端,路由器适配器,主题,会话侧边栏
列表,项目主页,会话工作区,文件页面,自定义工具行,应用程序
签到): skills/kortix-ui/references/custom-dashboard.md. 它的样本是
一个Vite + React 19应用程序的副本,该应用程序的类型检查、构建和运行都是基于
生活的API。每个条目都有它的道具和一个例子: references/modules.md.
布局
src/<module>/ 每个人承担一项责任。文件导入自己的模块或
下层; internal 没有公共入口,也没有停车位。静态向上
仍然存在的进口 (26个,主要是 customize → workspace 和 internal →
customize)均列于 src/module-cycles.test.ts (KNOWN_UPWARD): 一个新的
其中一个无法通过测试,名单只会缩小。
| 层 | 模块 | 条目 |
|---|---|---|
| 0 | lib, hooks, icons, providers, i18n, theme, styles, types | ., ./icons, ./i18n, ./theme |
| 1 | primitives: 设计系统组件 | ./<name> 每个文件 |
| 2 | internal: 存储,挂钩,助手,沙箱文件数据层,共享和布局位 | 无 |
| 3 | markdown (与 genui, setup-links), diff | ./markdown, ./genui, ./diff |
| 4 | changes, files (tree, runtime, project, viewer, renderers, editor, drives), terminal, apps, browser, access | ./changes, ./files, ./file-viewer, ./terminal, ./apps, ./browser, ./access |
| 5 | session (thread, turn, composer, tool, prompts, status, models, header, actions, scope, overrides, print, media, outcomes, pickers, panel) | ./session, ./composer, ./session-panel |
| 6 | customize:项目配置页面 | ./customize |
| 7 | workspace: 组合屏幕 | ./workspace |
“沙盒文件”是实时会话的工作区(files/runtime); “项目
“文件”是礼品支持的回购(files/project)。每个条目都是显式的
桶 (否 export *),列于 scripts/gen-exports.mjs ENTRIES. 每一个
其他模块文件只能通过其条目访问。一个深键
(./<module>/<path>,没有semver) 中的每个文件都存在
scripts/deep-exports.json:模块文件apps/web,白标演示和
回购测试今天导入。 tests/unit/ui-deep-exports.test.ts 保留该列表
等于进口,所以一个新的深度进口是一个故意的编辑。
主题
@kortix/ui/theme 是类型化主题API。每个字段都是可选的;一个设置
字段保留Kortix默认值,并且 dark 颜色回落到科蒂克斯深
默认,从不到 light 价值。
import { themeToCss, ThemeScope, type KortixTheme } from '@kortix/ui/theme';
const brand: KortixTheme = {
light: { blue: '#4f46e5', ring: '#4f46e5', border: '#e0e7ff' },
dark: { blue: '#818cf8', ring: '#818cf8', border: '#312e81' },
radius: '0.25rem',
fonts: { sans: 'Inter, system-ui, sans-serif' },
};
<style>{themeToCss(brand)}</style> // the whole app (server-safe)
<ThemeScope theme={brand}>…</ThemeScope> // one subtree; its overlays portal into it
| 场 | 变量 | 控制 |
|---|---|---|
light, dark | --background, --foreground, --card, --popover, --primary, --secondary, --muted, --accent, --destructive (+ *-foreground), --border, --input, --ring, --focus-ring, --hover, --active, --selection, --sidebar-*, --kortix-*, --chart-*, --terminal-*,任何 --custom | 每种模式的颜色。 blue (--kortix-blue)是口音; ring 是重点; primary 是墨水 |
radius | --radius | 每一个 rounded-* 源自它(rounded − 6px… 2xl + 6 px) |
spacing | --spacing | 每个的密度 p-*, m-*, gap-*, size-* |
fonts.sans, fonts.mono | --font-family-sans, --font-family-mono | 正文和标题; 代码和id |
text, lineHeights | --text-<step>, --text-<step>--line-height | 型秤 |
shadows | --shadow-<step> | 海拔高度; 0 0 #0000 压平一个台阶 |
durations, easing | --duration-*, --ease-* | 运动 |
vars | 任何 --name | 两种模式都可以 |
THEME_COLOR_VARS 将每个颜色键映射到其变量。普通CSS也可以工作:
在您自己的代码中设置相同的变量 :root / .dark 导入后。从来没有
Fork一个组件。Roobert (--font-roobert) 仅适用于Kortix拥有的曲面。
apps/white-label/reskin/src/config/theme.ts 是参考。
它如何保持主题:
theme.css将每个顺风键映射到一个变量。共享名称的步骤 与他们的变量 (--text-sm,--shadow-md,--ease-default,光秃秃的rounded) 住在@theme inline reference,所以顺风从来不排放--x: var(--x)循环进入:root.- 每个基数传送门都要经过
withPortalContainer(src/lib/portal-container.tsx)。没有一个ThemeScope它的入口是document.body,和以前一样。 - 新组件只读取令牌:没有十六进制,没有调色板类,没有固定
半径。
apps/web必须看起来相同: 每个新变量默认为 它替换的值。
已知的问题
- 服务器呈现: 呈现会话或文件的条目 (
session,composer,session-panel,workspace,files,file-viewer,markdown,genui,customize) 从JS导入CSS (KaTeX和其他),因此plain Node无法评估它们 (ERR_UNKNOWN_FILE_EXTENSION ".css"). Vite SSR主机添加ssr: { noExternal: ['@kortix/ui'] }. 一个Next.js主机列表@kortix/ui在transpilePackages。或仅在客户端上加载这些块 (lazyClient). 根,theme,i18n,icons,diff,changes,terminal,browser,apps和access条目在节点中计算。 [email protected]声明一个React 18对等体和巢@radix-ui/[email protected].Commandworks on React 19.一个主机与strict-peer-dependencies必须允许cmdk的React peer。cmdk1.倍 更改选择模型command.tsx被调谐到,并且apps/web导入cmdk直接,所以升级本身就是改变。
规则
- 值:
kortix-brand技能(.agents/skills/kortix-brand/SKILL.md)。 发电机是.agents/skills/kortix-brand/scripts/generate-tokens.ts. - 组件:
kortix-design-system技能。 - 包内的导入是相对的。从来没有进口
apps/web代码。 @kortix/shared和@kortix/manifest-schema是私人的: 他们是 devDependencies,scripts/build.mjs内线dist从他们那里进口的东西。- 在此处编辑组件。
apps/web不保留再出口垫片 (tests/unit/web-no-ui-shims.test.ts);进口@kortix/ui/<name>或者入口。 package.jsonexports生成:pnpm --filter @kortix/ui gen-exports.src/exports.test.ts在漂移和不存在的导出目标上失败。src/styles/tokens.css,src/styles/fonts.css和theme地区的src/styles/theme.css是生成的。dist/styles.css是建立。永远不要手工编辑它们。
检查
pnpm --filter @kortix/ui typecheck
pnpm --filter @kortix/ui test src/exports.test.ts # exports map, targets, dist/styles.css drift
pnpm --filter @kortix/ui test src/skill.test.ts # skill names every barrel export; managed copy identical
bun .agents/skills/kortix-brand/scripts/generate-tokens.ts --check
.agents/skills/kortix-brand/scripts/audit.sh packages/ui/src