From 9fce0fdaa0bf961ba08b6eb8883c2d1801b340f8 Mon Sep 17 00:00:00 2001 From: 18628386721 <18628386721@163.com> Date: Mon, 27 Jul 2026 21:08:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E9=9D=A2=E6=8B=86=E5=88=86=E2=91=A3:?= =?UTF-8?q?=E9=A9=BE=E9=A9=B6=E8=88=B1=E9=A6=96=E9=A1=B5=20=E2=80=94?= =?UTF-8?q?=E2=80=94=20=E5=BA=A6=E9=87=8F=E6=94=B9=E6=8E=A5=20report=20?= =?UTF-8?q?=E7=9C=9F=E5=85=AB=E6=8C=87=E6=A0=87,=E7=BA=A0=E6=AD=A3?= =?UTF-8?q?=E7=8E=87/DMN=20=E4=B8=8D=E5=86=8D=E6=81=92=E4=B8=BA=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 治理台原先调 memory 的五指标版,还把 submissions/corrections/judgements 硬塞 0 —— 仪表在说谎。八指标本就在 report 里算好,这里接上,与工作台汇报同源同口径。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m --- .../contracts/src/api/governance.d.ts | 31 +- apps/api/src/app/routes/governance-ops.ts | 26 +- apps/api/src/modules/report/CONTRACT.md | 8 +- apps/web/src/app/gov-shell.tsx | 6 +- apps/web/src/features/gov-memory/index.ts | 2 +- .../features/gov-memory/internal/panel.tsx | 268 ++++++++++++------ .../features/gov-process/internal/panel.tsx | 23 +- apps/web/src/shared/gov/gov-api.ts | 24 ++ docs/debt.md | 7 + packages/contracts/src/api/governance.ts | 21 +- packages/ui-kit/src/Ico.tsx | 3 + 11 files changed, 292 insertions(+), 127 deletions(-) diff --git a/api-report/packages/contracts/src/api/governance.d.ts b/api-report/packages/contracts/src/api/governance.d.ts index 23ace64..3b51c98 100644 --- a/api-report/packages/contracts/src/api/governance.d.ts +++ b/api-report/packages/contracts/src/api/governance.d.ts @@ -1749,17 +1749,46 @@ export declare const govWorkflowRoute: { }, z.core.$strip>>; }, z.core.$strip>; }; -/** 自主率与纠正率**成对读**,复合为有效自主率 —— 单看自主率会奖励「猛冲后返工」 */ +/** + * 度量八指标(OS 8.3)—— 运营驾驶舱的仪表读数。 + * + * 出参直接复用 `reportMetricsSchema`:**与工作台汇报同源同口径**。 + * 治理台若另算一套,同一个组织会读出两个自主率 —— 那时候争的就不是数,是谁的表准。 + * 自主率与纠正率**成对读**,复合为有效自主率 —— 单看自主率会奖励「猛冲后返工」。 + */ export declare const govMetricsRoute: { readonly method: "GET"; readonly path: "/api/gov/metrics"; + /** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */ + readonly query: z.ZodObject<{ + period: z.ZodOptional>; + }, z.core.$strip>; + /** `period` 原样回声:客户端不传时,由它知道服务端用的是哪个窗口 */ readonly output: z.ZodObject<{ autonomy: z.ZodNumber; correction: z.ZodNumber; effectiveAutonomy: z.ZodNumber; interruption: z.ZodNumber; + breakpoint: z.ZodNumber; dmnCoverage: z.ZodNumber; + unitCost: z.ZodObject<{ + tokens: z.ZodNumber; + minutes: z.ZodNumber; + humanCalls: z.ZodNumber; + }, z.core.$strip>; + terminalMix: z.ZodRecord; taskCount: z.ZodNumber; + period: z.ZodEnum<{ + day: "day"; + week: "week"; + month: "month"; + year: "year"; + }>; }, z.core.$strip>; }; export type GovTasksOutput = z.infer; diff --git a/apps/api/src/app/routes/governance-ops.ts b/apps/api/src/app/routes/governance-ops.ts index 7f5b51c..1394ca7 100644 --- a/apps/api/src/app/routes/governance-ops.ts +++ b/apps/api/src/app/routes/governance-ops.ts @@ -410,21 +410,27 @@ export function registerGovernanceOpsRoutes(app: Hono, container: Contai * 度量仪表 —— 自主率与纠正率**成对读**,复合为有效自主率。 * 只读近期任务的任务记忆聚合;度量本身不载权力。 */ + /* + * 度量八指标 —— 走 report 模块的**全组织**投影(staffId 传 undefined)。 + * + * 此前这里调的是 memory 的五指标版,且把 submissions / corrections / + * judgements 四个入参硬塞 0 —— 纠正率与 DMN 覆盖率于是结构性恒为 0, + * 仪表在说谎。八指标本就在 report 里算好(§report/contract.ts:「治理台记忆区的 + * 仪表读数,同源同口径」),这里只是把它接上,不另起一套口径。 + */ app.get(govMetricsRoute.path, async (c) => { const denied = await readMemory(c.get('account')); if (denied) return c.json(denied, denyStatus(denied)); + const parsed = govMetricsRoute.query.safeParse(c.req.query()); + if (!parsed.success) { + const body: ApiError = { code: 'validation-failed', message: '统计窗口不合法', detail: parsed.error.issues }; + return c.json(body, 400); + } + const period = parsed.data.period ?? 'month'; const { staff } = await container.org.listStaff(); const nested = await Promise.all(staff.map((s) => container.task.listByOwner(s.id))); - const taskIds = nested.flat().map((t) => t.id); - const metrics = await container.memory.metrics({ - taskIds, - submissions: 0, - corrections: 0, - taskCount: taskIds.length, - deterministicJudgements: 0, - totalJudgements: 0, - }); - return c.json({ ...metrics, taskCount: taskIds.length }); + const metrics = await container.report.metrics(undefined, period); + return c.json({ ...metrics, taskCount: nested.flat().length, period }); }); /* ── 系统管理 · 通道实例 ────────────────────────────────────────── */ diff --git a/apps/api/src/modules/report/CONTRACT.md b/apps/api/src/modules/report/CONTRACT.md index 2df4983..bd183c4 100644 --- a/apps/api/src/modules/report/CONTRACT.md +++ b/apps/api/src/modules/report/CONTRACT.md @@ -24,5 +24,11 @@ - **口径挂归侧与事件流,零埋点**:输入只有归侧日志行与任务终态,没有第三种埋点 - **自主率与纠正率成对读**,复合为有效自主率 —— 单看任一都会被优化坏 - **单位成本的分母只认放行通过**:跑得多不等于产出多 - - 度量是**只读投影**,不设独立模块(§4 注);治理台记忆区与工作台汇报同源同口径 + - 度量是**只读投影**,不设独立模块(§4 注);治理台驾驶舱与工作台汇报同源同口径 - 周期窗口左闭右开、**周一为首日**、按 UTC 切分(单组织单时区) + +近变(2026-07-27): + - 模块面**未变**。app 层的 `GET /api/gov/metrics`(运营驾驶舱)改接本模块的 + `metrics(undefined, period)` —— 此前它调 memory 的五指标版并把 submissions / + corrections / judgements 硬塞 0,纠正率与 DMN 覆盖率结构性恒为 0。 + 「治理台与工作台汇报同源同口径」这一条,从此在代码里也成立。 diff --git a/apps/web/src/app/gov-shell.tsx b/apps/web/src/app/gov-shell.tsx index 8392eb0..ddbf56f 100644 --- a/apps/web/src/app/gov-shell.tsx +++ b/apps/web/src/app/gov-shell.tsx @@ -30,7 +30,7 @@ import { TriggerPage } from '@features/gov-trigger'; import { ActPage, LibraryHome } from '@features/gov-activity'; import { ToolPage, ToolsHome } from '@features/gov-tool'; import { DataHome, ObjPage } from '@features/gov-data'; -import { MemPage, MemoryHome } from '@features/gov-memory'; +import { CockpitHome, KnowledgeHome, MemPage } from '@features/gov-memory'; import { ChanPage, LlmPage, SystemHome } from '@features/gov-system'; /* ── URL 编解码:`#///[/:…]` ────────────────── */ @@ -341,11 +341,11 @@ function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) tools: ToolsHome, data: DataHome, // 运营中心 - cockpit: MemoryHome, + cockpit: CockpitHome, tasks: TasksHome, people: PeopleHome, ledgers: LedgersPlaceholder, - knowledge: MemoryHome, + knowledge: KnowledgeHome, system: SystemHome, }[zone]; return

; diff --git a/apps/web/src/features/gov-memory/index.ts b/apps/web/src/features/gov-memory/index.ts index 645bf50..e02e0c9 100644 --- a/apps/web/src/features/gov-memory/index.ts +++ b/apps/web/src/features/gov-memory/index.ts @@ -2,4 +2,4 @@ * feature `gov-memory` 唯一出口(编码规范 §9.1)。 * 记忆管理:度量仪表、活动记忆四层 */ -export { MemoryHome, MemPage } from './internal/panel.tsx'; +export { CockpitHome, KnowledgeHome, MemPage } from './internal/panel.tsx'; diff --git a/apps/web/src/features/gov-memory/internal/panel.tsx b/apps/web/src/features/gov-memory/internal/panel.tsx index c110315..b43e03b 100644 --- a/apps/web/src/features/gov-memory/internal/panel.tsx +++ b/apps/web/src/features/gov-memory/internal/panel.tsx @@ -1,91 +1,200 @@ /** - * feature `gov-memory` —— 记忆管理:度量仪表、活动记忆四层 + * feature `gov-memory` —— 运营中心的两处:**经营驾驶舱**(度量八指标)与**知识库**(活动记忆四层)。 * * 自 `~/coding/JCAI_PRD/governance/_components/Governance.tsx` 平移: * DOM 结构、类名(`g-*` + 色调 `t-*`)与文案原样保留,mock 常量换成服务端投影(README 平移规则 1/2/4)。 - * 区首页只负责导航,治理动作一律落在下钻详情页的**就地确认带**(影响预览三必答)。 + * + * 原本这两件同挂在「记忆管理」一区,是两种心智硬挤在一起: + * 看指标问的是「今天跑得怎样」,管沉淀问的是「学到了什么、放不放行」。三面拆分后各自成页。 */ import { useState } from 'react'; -import { Chip, Clear, Kpi, NList, NRow, Note, PageHead, QueryGate } from '@web-shared/gov/primitives.tsx'; +import { TERMINAL_STATE_LABEL } from '@contracts'; +import type { TerminalState } from '@contracts'; +import { Chip, Clear, Kpi, NList, NRow, Note, PageHead, QueryGate, Todo } from '@web-shared/gov/primitives.tsx'; import type { Nav } from '@web-shared/gov/gov-route.ts'; import { useGovActivities, useGovMemory, useGovMetrics, + useGovPackages, + useGovPkgReports, + useGovTasks, } from '@web-shared/gov/gov-api.ts'; export type HomeProps = { tab: string; nav: Nav }; -export function MemoryHome({ tab, nav }: HomeProps) { +const pct = (v: number | undefined) => Math.round((v ?? 0) * 100); + +/* ---------------- 经营驾驶舱(运营首页) ---------------- */ + +/** + * 看企业今天跑得怎样 —— 先看**要不要管**(异常三卡),再看**跑得好不好**(度量八指标)。 + * 异常在上:开屏第一眼该回答的是「有没有事要我处置」,不是「自主率几个点」。 + */ +export function CockpitHome({ nav }: HomeProps) { const metrics = useGovMetrics(); - const activities = useGovActivities(); - const [picked, setPicked] = useState(undefined); - const memory = useGovMemory(picked); - - if (tab === 'layers') { - return ( - <> - - 活动记忆四层:事实层 / 模式层 / 裁决层 / 例外层。写入唯蒸馏,条目必挂证据链; - 记忆不载权力——强制力只经候选增量 + 回放 + 人放行进入业务模型。 - -

- {(activities.data?.activities ?? []).map((a) => ( - - ))} -
- - {(memory.data?.entries ?? []).map((e) => ( - 候选 · {e.incrementState} : 已成条目} - onClick={() => nav.push({ k: 'mem', id: e.id })} - /> - ))} - {picked !== undefined && (memory.data?.entries ?? []).length === 0 && ( - 该活动尚无记忆条目——终态封卷触发蒸馏钩子后产出。 - )} - - - ); - } - + const tasks = useGovTasks(); + const packages = useGovPackages(); + const reports = useGovPkgReports(packages.data?.packages ?? []); const m = metrics.data; - const pct = (v: number | undefined) => Math.round((v ?? 0) * 100); + + const workflows = tasks.data?.workflows ?? []; + const taskRows = tasks.data?.tasks ?? []; + const abnormal = workflows.filter((w) => w.state === 'abnormal').length; + const running = taskRows.filter((t) => t.terminal === undefined).length; + const draftBad = (packages.data?.packages ?? []).filter( + (p) => p.state !== 'published' && reports.data?.get(`${p.id}@${p.version}`)?.publishable === false, + ).length; + + const mix = m?.terminalMix ?? {}; + const mixTop = Object.entries(mix) + .filter(([, v]) => v > 0) + .sort((a, b) => b[1] - a[1]); + return ( - <> -
- - - - - -
- - 自主率与纠正率成对读,复合为有效自主率——单看自主率会奖励「猛冲后返工」。度量只是仪表,不载权力。 - - + <> +
+ + + +
+ {abnormal > 0 ? ( +
+ nav.jump('tasks', 'todo') }]} + /> +
+ ) : ( + 没有异常工作流 —— 在途的都还在正常走。 + )} + +
+ + + + +
+
+ + + + `${TERMINAL_STATE_LABEL[k as TerminalState] ?? k} ${pct(v)}%`) + .join(' · ') + } + /> +
+ + 自主率与纠正率成对读,复合为有效自主率——单看自主率会奖励「猛冲后返工」。度量只是仪表,不载权力。 + 八指标与工作台的汇报同源同口径,统计窗口:近一{m?.period === 'day' ? '日' : m?.period === 'week' ? '周' : m?.period === 'year' ? '年' : '月'}。 + +
); } -/* ---------------- ⑨ 系统管理 ---------------- */ +/* ---------------- 知识库 ---------------- */ + +/** 活动记忆四层 + 候选增量的放行口 —— 记忆的终点是成为模型,那道门在这里 */ +export function KnowledgeHome({ nav }: HomeProps) { + const activities = useGovActivities(); + const [picked, setPicked] = useState(undefined); + const memory = useGovMemory(picked); + + return ( + <> + + 活动记忆四层:事实层 / 模式层 / 裁决层 / 例外层。写入唯蒸馏,条目必挂证据链; + 记忆不载权力——强制力只经候选增量 + 回放 + 人放行进入业务模型。 + +
+ {(activities.data?.activities ?? []).map((a) => ( + + ))} +
+ + {(memory.data?.entries ?? []).map((e) => ( + 候选 · {e.incrementState} + ) : ( + 已成条目 + ) + } + onClick={() => nav.push({ k: 'mem', id: e.id })} + /> + ))} + {picked === undefined && 选一个活动,看它这些年学到了什么。} + {picked !== undefined && (memory.data?.entries ?? []).length === 0 && ( + 这个活动还没有记忆条目 —— 任务落终态封卷、蒸馏钩子跑过之后,沉淀会出现在这里。 + )} + + + ); +} + +/* ---------------- 记忆条目详情 ---------------- */ export function MemPage({ id }: { id: string }) { const activities = useGovActivities(); @@ -94,22 +203,17 @@ export function MemPage({ id }: { id: string }) { const e = (memory.data?.entries ?? []).find((x) => x.id === id); if (e === undefined) return 记忆条目需从对应活动进入。; return ( - <> - - {e.evidenceCount} 条证据 - {e.incrementState !== undefined && 候选增量 · {e.incrementState}} - - } - desc="写入唯蒸馏,条目必挂证据链;记忆不载权力——强制力只经候选增量 + 回放 + 人放行进入业务模型。" - /> - + + {e.evidenceCount} 条证据 + {e.incrementState !== undefined && 候选增量 · {e.incrementState}} + + } + desc="写入唯蒸馏,条目必挂证据链;记忆不载权力——强制力只经候选增量 + 回放 + 人放行进入业务模型。" + /> ); } - -/* ---------------- 系统 ---------------- */ - diff --git a/apps/web/src/features/gov-process/internal/panel.tsx b/apps/web/src/features/gov-process/internal/panel.tsx index a1cd8f3..1bb5c97 100644 --- a/apps/web/src/features/gov-process/internal/panel.tsx +++ b/apps/web/src/features/gov-process/internal/panel.tsx @@ -35,7 +35,7 @@ import { Todo, } from '@web-shared/gov/primitives.tsx'; import type { Nav } from '@web-shared/gov/gov-route.ts'; -import { useGovActivities, useGovPackages, useGovStaff, useGovTriggers } from '@web-shared/gov/gov-api.ts'; +import { useGovActivities, useGovPackages, useGovPkgReports, useGovStaff, useGovTriggers } from '@web-shared/gov/gov-api.ts'; import { FlowFigureBlock } from '@web-shared/gov/flow/FlowFigure.tsx'; import { packageToGraph } from '@web-shared/gov/flow/pkg-to-graph.ts'; import { mergeGraphIntoPackage } from '@web-shared/gov/flow/graph-to-pkg.ts'; @@ -47,23 +47,6 @@ export type HomeProps = { tab: string; nav: Nav }; /* ── 报告:画图即校验,列表与详情同一来源 ─────────────────────────────── */ -/** 每个包一份闭合报告 —— 磁贴上的「边 n/m」、待处置清单、发布门都读它 */ -function usePkgReports(packages: readonly ProcessPackage[]) { - return useQuery({ - queryKey: ['gov', 'process-reports', packages.map((p) => `${p.id}@${p.version}`).join(',')], - enabled: packages.length > 0, - queryFn: async () => { - const entries = await Promise.all( - packages.map( - async (p) => - [`${p.id}@${p.version}`, await call(validateProcessRoute, { body: { pkg: p } })] as const, - ), - ); - return new Map(entries); - }, - }); -} - /** 边闭合数:边总数 − 有违规的边(同一条边多处违规只记一次) */ function edgeCheckOf(pkg: ProcessPackage, report: ClosureReport | undefined) { const total = pkg.edges.length; @@ -84,7 +67,7 @@ export function WorkflowsHome({ tab, nav }: HomeProps) { function PackagesHome({ tab, nav }: HomeProps) { const q = useGovPackages(); const packages = q.data?.packages ?? []; - const reports = usePkgReports(packages); + const reports = useGovPkgReports(packages); const reportOf = (p: ProcessPackage) => reports.data?.get(`${p.id}@${p.version}`); const counts = (p: ProcessPackage) => ({ activity: p.nodes.filter((n) => n.kind === 'activity').length, @@ -860,7 +843,7 @@ export function EdgesPage({ id }: { id: string }) { const [pid, versionText] = id.split('@'); const q = useGovPackages(); const pkg = (q.data?.packages ?? []).find((p) => p.id === pid && String(p.version) === versionText); - const reports = usePkgReports(pkg === undefined ? [] : [pkg]); + const reports = useGovPkgReports(pkg === undefined ? [] : [pkg]); if (pkg === undefined) { return ( diff --git a/apps/web/src/shared/gov/gov-api.ts b/apps/web/src/shared/gov/gov-api.ts index 6692cfd..f50c5e8 100644 --- a/apps/web/src/shared/gov/gov-api.ts +++ b/apps/web/src/shared/gov/gov-api.ts @@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query'; import type { + ClosureReport, GovActivitiesOutput, GovApprovalsOutput, GovChannelsOutput, @@ -21,6 +22,7 @@ import type { GovToolsOutput, GovTriggersOutput, GovWorkflowOutput, + ProcessPackage, } from '@contracts'; import { browseLedgerRoute, @@ -39,6 +41,7 @@ import { listStaffRoute, listToolsRoute, listTriggersRoute, + validateProcessRoute, } from '@contracts'; import { call } from '@web-shared/typed-fetch.ts'; @@ -81,6 +84,27 @@ export const useGovGrants = (positionId: string | undefined): UseQueryResult => useQuery({ queryKey: ['gov', 'process'], queryFn: () => call(listProcessPackagesRoute) }); +/** + * 每个包一份闭合报告 —— 磁贴上的「边 n/m」、待补清单、发布门、驾驶舱的未闭合草稿数都读它。 + * 一处算、多处读:两张首页各算一遍就会各说各话。 + */ +export const useGovPkgReports = ( + packages: readonly ProcessPackage[], +): UseQueryResult> => + useQuery({ + queryKey: ['gov', 'process-reports', packages.map((p) => `${p.id}@${p.version}`).join(',')], + enabled: packages.length > 0, + queryFn: async () => { + const entries = await Promise.all( + packages.map( + async (p) => + [`${p.id}@${p.version}`, await call(validateProcessRoute, { body: { pkg: p } })] as const, + ), + ); + return new Map(entries); + }, + }); + export const useGovTriggers = (): UseQueryResult => useQuery({ queryKey: ['gov', 'triggers'], queryFn: () => call(listTriggersRoute) }); diff --git a/docs/debt.md b/docs/debt.md index a063f8e..f6e7695 100644 --- a/docs/debt.md +++ b/docs/debt.md @@ -53,3 +53,10 @@ - 缩短页签文字(沟通 / 成果 / 详情 / 日志)—— 省 ~100px,但文案变短 - 翻页囊只留箭头、页码收进 title —— 省 ~50px,但看不见「第几件/共几件」 - 页签条换行两排 —— 全都放得下,但工作区正文少一行高 + +## 驾驶舱 · 死信卡(2026-07-27) + +运营驾驶舱首屏收了「异常工作流 / 未闭合草稿 / 在途任务」三卡,**没有死信卡**。 +死信目前只以治理者消息要素送出(`deadLetters.report` → `message`),没有一个按 kind 计数的 +查询面 —— 要补这一卡,得给 system_message 加一条按 kind 聚合的治理路由。 +在那之前,驾驶舱不摆一个查不出数的卡位。 diff --git a/packages/contracts/src/api/governance.ts b/packages/contracts/src/api/governance.ts index e818778..ebad5bc 100644 --- a/packages/contracts/src/api/governance.ts +++ b/packages/contracts/src/api/governance.ts @@ -9,6 +9,7 @@ import { positionIdSchema, staffIdSchema, taskIdSchema } from '../id.ts'; import { closureReportSchema, processPackageSchema, triggerConfigSchema } from '../process-package.ts'; import { controlSemanticSchema, taskRuntimeStateSchema, terminalStateSchema, workflowStateSchema } from '../terminal.ts'; import { paradigmSchema } from '../manifest.ts'; +import { reportMetricsSchema, reportPeriodSchema } from './workbench.ts'; /* ── 员工管理 ─────────────────────────────────────────────────────────── */ @@ -393,18 +394,20 @@ export const govWorkflowRoute = { /* ── 记忆管理 · 度量仪表 ──────────────────────────────────────────────── */ -/** 自主率与纠正率**成对读**,复合为有效自主率 —— 单看自主率会奖励「猛冲后返工」 */ +/** + * 度量八指标(OS 8.3)—— 运营驾驶舱的仪表读数。 + * + * 出参直接复用 `reportMetricsSchema`:**与工作台汇报同源同口径**。 + * 治理台若另算一套,同一个组织会读出两个自主率 —— 那时候争的就不是数,是谁的表准。 + * 自主率与纠正率**成对读**,复合为有效自主率 —— 单看自主率会奖励「猛冲后返工」。 + */ export const govMetricsRoute = { method: 'GET', path: '/api/gov/metrics', - output: z.object({ - autonomy: z.number(), - correction: z.number(), - effectiveAutonomy: z.number(), - interruption: z.number(), - dmnCoverage: z.number(), - taskCount: z.number().int(), - }), + /** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */ + query: z.object({ period: reportPeriodSchema.optional() }), + /** `period` 原样回声:客户端不传时,由它知道服务端用的是哪个窗口 */ + output: reportMetricsSchema.extend({ taskCount: z.number().int(), period: reportPeriodSchema }), } as const satisfies RouteContract; /* ── 出参类型:契约自己命名,消费方不再 `z.infer` ──────────────────────── */ diff --git a/packages/ui-kit/src/Ico.tsx b/packages/ui-kit/src/Ico.tsx index 7173e05..0eb7ea2 100644 --- a/packages/ui-kit/src/Ico.tsx +++ b/packages/ui-kit/src/Ico.tsx @@ -29,6 +29,9 @@ export function Ico({ name, s = 18 }: { name: string; s?: number }) { "user-round": (<>), bot: (<>), dashboard: (<>), + hand: (<>), + unplug: (<>), + coins: (<>), mail: (<>), "mail-open": (<>), inbox: (<>),