npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

soma-kit

v0.1.11

Published

SOMA 第三条 UI 路线:写死模板与规则,DSL 显式传入,基础组件 + 数据组件

Readme

soma-kit

SOMA 的第三条 UI 路线(私有源码包,版本 0.1.0):模板与规则写死在 kit 内部,DSL 只显式传入数据结构、对象的样子与行为; 样式统一采用 Tailwind 类,不再书写原生 CSS。依赖方向保持单向:soma-design → soma-foundation → soma-core → soma-kit,不依赖 soma-ui、soma-lite。

组件只有两层:基础组件(处理单个值)与数据组件(处理单个对象或对象列表)。页面框、业务组件属于写死的模板外壳,不计入组件层。


核心设计原则

  1. 规整归对象引擎,异形归写死定制:
    • 标准通用的表格、筛选、表单、详情、普通卡片流交给对象渲染机制统一生成。只读态默认一律渲染为纯文本 Text,保持克制干净,视觉重心由使用者显式传入的组件与样式决定;
    • 异形结构、特殊边框、复杂非标布局绝不强行扭曲通用对象语法,一律通过写死结构样式或写死专属声明翻译来实现。
  2. 标准业务组件只保通用,特例靠应用包装与组合:
    • 内置标准业务组件(StandardCrud、MasterSubWorkstation 等)只负责跨行业通用的对象管理与协作流程,不掺杂特例配置参数;
    • 当通用 DSL 无法满足项目的特殊边框、特殊头尾或局部语法简写时,应用直接通过包装原语或组合原语快速沉淀自己的专属 DSL 翻译逻辑。

当前架构层级

  • 第 0 层:包骨架与语法真源: spec/kit-grammar.json(语法闭集、类型默认表、宽度拆分规则、语义色名表、诊断码)及其模式定义;src/theme/kit-theme.css 是 Tailwind v4 @theme 主题头,把语义色名映射到 --soma-* 令牌;具体令牌取值由 foundation 主题流水线在装配期求解。
  • 第 1 层:44 个基础组件(位于 src/base/,经 createKitBaseComponents() 取出组件字典交 core 注册;KIT_BASE_CLASS_CANDIDATES 汇总全部类串供样式编译):
    • 核心通用基础件(18 个):Text、Tag、Input、Textarea、Number、Select、Radio、Checkbox、Switch、Date、DateRange、Button、Link、TagInput、Upload、TimePicker、TreeSelect、Alert;
    • 交互与排版扩展(12 个):Range、Rating、Otp、Avatar、AvatarGroup、Kbd、KitProgress(shape: ring | bar)、Countdown、Swap、Fab、Image、MenuButton;其中通用 tooltip 属性支持 Button / Link / Switch / Swap / Kbd / Fab / MenuButton;
    • 高级选择与录入(8 个):Cascader、Combobox、ColorPicker、Mentions、Popover(confirm: true 即气泡确认框)、PriceText、QrCode、CommandPalette;并入既有件参数的形态:Radio variant: segmented、Checkbox variant: buttons、Number variant: stepper、Tag variant: dot(pulse)、MenuButton trigger: contextmenu;
    • 选项组竖排与纯图标(M 波 F1 / A12,页面 /ref/f1):Radio / Checkbox 的 orientation: horizontal | vertical(segmented / buttons 形态同样生效,竖排 segmented 用 ↑/↓ 移动);options 项键 icon(短字符)与 aria_label,icon 且 label 为空即纯图标选项,可访问名取 aria_label(缺省取 value);Navbar 下拉子项键 header: true(只读标题行,页面 /ref/f1)。
    • 选择与反馈类参数(M 波 G1b,既有件的闭集参数,页面 /ref/g1b):Select 的 multiple / display: chips | text | count / group / clearable(NN 波 G1:Select 与 Combobox 合并为同一份自绘实现 base/listbox-core.ts,Select 是不可输入的 select-only combobox,没有原生 <select>,表单值在 hidden input,键盘含 typeahead,页面 /ref/nn/select);Combobox 的 multiple / limit_tags / group 与选项键 desc / icon / disabled;Range 的 range({start, end} 双滑块)/ marks / value_label / orientation: vertical(NN 波 G3:恒为自绘 SVG 滑块,没有原生 range,表单值在 hidden input,RTL 下反向,页面 /ref/nn/range);命令式调用方的下拉统一用 mountKitSelectField、整块重建的容器用 createKitEmbedBag 释放嵌入件(soma-kit/data);Upload 的 list: picture(照片墙)/ manual / upload_text(抛 upload);Date / DateRange 的 disabled_dates(抛 reject)与 DateRange.presets(NN 波 G2:日历为自绘浮层,不用原生 <input type=date>;新增 week_start、Date.placeholder / clearable,页面 /ref/nn/date);KitProgress 的 indeterminate / buffer / shape: dots;TimePicker 的 seconds / hour12;Fab 的 direction / back_top;
    • 输入与标记类参数(M 波 G1a,既有件的闭集参数):Input 的 clearable / prefix / suffix / counter / password_toggle / variant: filled(suffix 可传 { action, countdown } 做发送验证码倒数,抛 suffix_click);Textarea 的 auto_size / counter;Tag 的 removable / clickable / icon / variant: outline | solid;Button 的 variant: tonal / icon_only;MenuButton 的 split;角标 badge / badge_max / badge_dot(Button / Link / MenuButton / Tag);Link 的 underline / external;Text 的 copyable;tooltip 接到 Tag / Avatar;
    • 动效展示件(5 个,src/base/motion/):TextRotate、Hover3d、HoverGallery、Aura、Marquee,全部严格遵循 motion-safe: 触发,系统开启减少动效时保持静止;
    • 独立排版件(1 个):KitDivider(支持横线、竖线、带文字与各色语气)。
  • 第 2 层:数据组件:
    • 对象渲染机制(src/object-renderer/):将 KitObject 转换为中间表示,经双发射器输出静态四件套片段(静态 HTML、_ui.yaml、_event.yaml)与候选类,或生成表单属性;支持单一对象排布(cols、matrix、rows、inline)与集合排布(table、cards、stack、treeTable);
    • 内置写死形状数据组件(45 个,登记表位于 src/data/static-registry.ts): 涵盖树与结构(Tree、Steps、Timeline、Gantt)、流程与审批(ApprovalFlow、Checklist、FlowEditor(内置节点配置面板:nodeTypes.<类型>.fields 里写 kind 的字段出现在选中节点的右侧面板,改动抛 change 与 node_change)、RuleBuilder)、指标与分析(StatGroup、Chart、TrendLine、AmountBreakdown)、协作与交互(CommentThread、RecordCard、Transfer、PermissionMatrix、FieldMapper、PermissionTree、AssignmentPicker、Kanban、RowEditor、AsyncResultPanel、RequestConsole、Questionnaire、TourGuide)、导航与外壳(Menu、Navbar、Breadcrumbs、Watermark、AnchorNav、Hero、Carousel、Dock、Diff、Mockup、Footer、PricingTable、ItemList、CodeView、Calendar、ImageViewer、MediaGrid、EmojiReactionBar、RichTextEditor、Pager、RecordPicker);对标补齐(M 波 G4):Tree 加 checkable / propagation / lazy / filter,RowEditor 加 number / money / date 列、footer: sum、min_rows / max_rows,Chart 加 combo、ref_lines、legend、value_format,ItemList 加条目 group / leading / trailing,RecordCard 加 variant / clickable / media,RichTextEditor 加 source: markdown(只读),新增 RecordPicker 弹窗选记录(抛 pick { value, values, item, items } 供 ref.fill 回填);
    • 可写静态数据组件自动接线:当写死形状数据组件契约同时包含 emits.submit 与 props.object,且所在数据对象声明了 behavior.write 时,编译器自动注入 object(若契约包含 scope 同时注入 scope),并将 <锚点>.submit 默认接至 ACTION | data.submit(显式书写 view.on.submit 时以显式为准);
    • 数据组件包装与组合原语:wrapKitDataComponent 与 composeKitDataComponents,支持单组件样式固化、多组件拼装、属性转换与子事件转发;
    • 协作数据组件:持有草稿与局部校验的 KitObjectForm,以及承接表格行动作分流的 KitTableActions。
  • 第 3 层:业务组件、模板外壳与编译器:
    • 内置业务组件(15 个,位于 src/biz/;M 波 G5 新增 ResultPage 结果页 / 异常页、ImportWizard 导入向导,配协作组件 KitImportHub;StandardCrud 浮层可声明 editOverlay(只读浮层点「编辑」切到编辑浮层),RecordDetail 的 record.subsLayout: tabs 把关联子对象收进分段切换页签):StandardCrud(单实体增删改查)、MasterSubWorkstation(主从协同工作台)、CategoryCrud(分类条目联动)、MultiObjectBoard(多对象组合看板)、SingleObjectForm(独立单对象表单)、ApprovalDesk(三对象审批台)、RecordDetail(单记录详情台)、StepForm(分步向导表单)、NotificationCenter(通知中心)、AuthPanel(账户权限面板)、SettingsCenter(设置中心)、CheckoutPage(结算页)、LandingPage(落地页);
    • 页面模板(KIT_PAGE_TEMPLATES,自带 product.config,不在内置闭集,应用经 defineKitApp({ bizComponents }) 取用):SignInPage 登录 / 注册 / 找回 / 二次验证页,包装 SingleObjectForm,config 取 layout(center 居中卡片 / split 左品牌右表单)、brand、links、social;链接与第三方按钮只执行具名动作,不处理也不存储任何凭据;新增数据组件 ResultPanel(success / error / info / warning / 403 / 404 / 500 / empty);
    • 无界面协作数据组件:KitCrudBridge(焦点切片与增删改查协调)、KitLinkHub(选择切片与主子联动)、KitStepHub(步骤切片与向导数据汇总);
    • 专属配置通道 product.config 与编译门禁:内置标准业务组件默认 acceptConfig 为假,页面向其传入 product.config 会被编译器直接拦截并报错 KIT-BIZ-003;包装组件或自定义业务组件开启 acceptConfig: true 后方可合法承接专属配置;
    • 业务组件包装与组合原语:wrapKitBizComponent(单业务组件包装、输入翻译与装饰外壳)与 composeKitBizComponents(多业务组件前缀隔离、无冲突组合与异形外壳);
    • 页面框(src/frames/):内置 single-frame、blank-frame、top-main-frame、tabs-frame、split-frame、workspace-frame,页面框均支持 page.breadcrumbs 面包屑;带页头的框另支持 page.actions / back / status / max_width / sticky(动作区窄屏收进下拉、返回对应信封 nav.back);workspace-frame 另有 theme_panel(观看者主题抽屉)、fullscreen、user.avatar / accounts / guest、sidebar_footer 与菜单 divider,以及 share_hash(标签与地址栏 hash 同步)、alerts(顶部告警条)、systems + navbar.end[].panel: systems(系统切换面板,数据组件 SystemSwitcher);
    • 五步编译器(src/compile/)与应用装配面(src/app/):严格进行契约校验、对象关系展开、对象渲染生成、样式合并与四件套产物构建。

质量体系与测试状态

soma-kit 拥有严密的质量保障网络,全层级代码、编译产物与运行时行为均已通过自动化验证:

  • 单元测试与集成测试:全量测试套件通过(125 个测试文件、1,571 条测试用例全部通过,零失败);
  • 端到端页面回归:包含 30 页真实业务示例(位于 ../soma-kit-demo/pages/*.kit.yaml),在真实浏览器与模拟运行时环境下逐页全编译、无诊断、零冲突,页面 URL 唯一,产物四件套完全符合 core 标准规范。

四大包装与组合原语(soma-kit/app 公开导出)

为彻底解决“业务个性化需求侵蚀通用框架”的难题,soma-kit/app 对外公开导出四大包装与组合原语,均在编译期执行确定性展开:

1. wrapKitDataComponent(inner, options)

包装单个内置或已有数据组件。复用底层组件的模板与控制器,通过纯函数 adapt 将外部业务数据或简写参数翻译为底层属性,适合固化边框样式、限定业务字段或写死映射逻辑:

import { wrapKitDataComponent } from 'soma-kit/app';
import { createRecordCard } from 'soma-kit/data';

export const AppVipOrderCard = wrapKitDataComponent(createRecordCard(), {
  name: 'AppVipOrderCard',
  props: { highlight: { type: 'boolean', default: false } },
  adapt(props) {
    const data = (props.data ?? {}) as Record<string, unknown>;
    return {
      data: {
        title: String(data.orderNo ?? ''),
        status: data.vip ? '重点单' : '常规单',
        tone: data.vip ? 'primary' : 'neutral',
      },
      class: props.highlight ? 'border-2 border-primary shadow-md' : 'border border-border',
    };
  },
});

2. composeKitDataComponents(options)

将多个数据组件(或基础组件)拼装在同一个自定义网格或异形布局壳中,对外暴露为一个标准的写死形状数据组件(layer: 'data')。自动合成服务依赖,深度转发属性变更与子事件:

import { composeKitDataComponents } from 'soma-kit/app';
import { createStatGroup, createTimeline } from 'soma-kit/data';

export const AppOrderCockpit = composeKitDataComponents({
  name: 'AppOrderCockpit',
  emits: { metric_pick: { payload: { item: { type: 'object' } } } },
  rootClass: '@container grid grid-cols-12 gap-4 rounded-xl border border-border p-4',
  parts: {
    metrics: {
      component: createStatGroup,
      slotClass: 'col-span-12 @md:col-span-5',
      adaptProps: (p) => ({ data: p.data?.summary ?? {}, selectable: true }),
      forwardEvents: { select: 'metric_pick' },
    },
    events: {
      component: createTimeline,
      slotClass: 'col-span-12 @md:col-span-7',
      adaptProps: (p) => ({ data: p.data?.logs ?? [] }),
    },
  },
});

3. wrapKitBizComponent(inner, options)

包装单个已有业务组件(如 StandardCrud),开启 product.config 配置通道。支持前置输入翻译 adaptInput 与后置外层外壳包裹 wrapHtml,保证标准组件零污染:

import { wrapKitBizComponent } from 'soma-kit/app';
import { StandardCrud } from 'soma-kit/biz';

export const AppBorderedCrud = wrapKitBizComponent(StandardCrud, {
  kind: 'AppBorderedCrud',
  extraAreas: ['shell'],
  candidates: ['rounded-xl border-2 border-primary p-6 shadow-md'],
  adaptInput(input, _ctx, diagnostics) {
    if (input.config?.border !== 'glow') {
      diagnostics.push({ code: 'KIT-BIZ-003', path: 'product.config.border', message: 'border 仅支持 glow' });
    }
    return input;
  },
  wrapHtml(innerHtml, { areaClass }) {
    return `<div data-kit-wrapped="glow" class="${areaClass('shell', 'rounded-xl border-2 border-primary p-6 shadow-md')}">${innerHtml}</div>`;
  },
});

4. composeKitBizComponents(options)

将多个业务组件组合为复合业务产品。编译器为每个子部件分配独立的前缀 ${ctx.pageId}_${partKey},彻底隔离内部数据对象名、锚点与区域切片,杜绝 KIT-PAGE-002 冲突;各子部件生成的片段经 mergeFragments 统一合并后,由 renderShell 填入自定义外壳:

import { composeKitBizComponents } from 'soma-kit/app';
import { MultiObjectBoard, StandardCrud } from 'soma-kit/biz';

export const AppCommandDesk = composeKitBizComponents({
  kind: 'AppCommandDesk',
  areas: ['headerArea', 'bodyArea'],
  parts: {
    kpi: { component: MultiObjectBoard, roleMap: { panels: 'kpiPanels' } },
    crud: { component: StandardCrud, roleMap: { entity: 'entity' }, forwardOverlays: true },
  },
  renderShell(partHtml, { rootClass, areaClass }) {
    return `<div class="${rootClass}">`
      + `<header class="${areaClass('headerArea', 'p-4 bg-surface-sunken')}">${partHtml.kpi}</header>`
      + `<main class="${areaClass('bodyArea', 'p-6 bg-surface')}">${partHtml.crud}</main>`
      + `</div>`;
  },
});

工程目录规范与三要素(HTML · DSL 翻译 · Tailwind)存放准则

总则:有自己 DSL 的件一律一件一目录,目录里放自己的 DSL 翻译(shell / compile / index)与对核心对象引擎的引用(emitObject / fragmentFromEmit 等);业务组件(含包装件、组合件、领域包 desk)和页面框(翻译 frame.config)属此类。基础组件和数据组件只吃 props、没有编译期 DSL 翻译,一件一个 .ts,不按行数拆;以后某个数据组件长出编译期翻译,就升级为目录。

无论是 soma-kit 内部还是外部业务应用,HTML 结构、DSL 翻译、Tailwind 样式一律存放在职责分离的纯 TypeScript 模块中(全工程除唯一的主题头 kit-theme.css 外不建零散 .html / .css 文件,保证服务端与浏览器同构打包零磁盘文件读取隐患,且可脱离浏览器直接对纯函数跑毫秒级单测):

| 要素 | 存放位置与物理形态 | 核心纪律 | |---|---|---| | HTML 结构壳 | 业务组件一件一目录,放 shell.ts(纯函数拼装 HTML 串);包装/组合业务组件在目录内 index.ts 的 wrapHtml / renderShell;基础/数据组件一件一个 .ts,templateHtml 与控制器同在该文件 | 锚点 data-soma-id 以 pageId 为前缀、双下划线 __ 分段、零点号;浮层外层只挂 hidden 属性 | | DSL 翻译逻辑 | 定制业务组件放 compile.ts;包装业务组件放 adaptInput(翻译 product.config,有翻译时可单放目录内 compile.ts);包装/组合数据组件的 adapt / adaptProps 与数据规整函数写在同一个 .ts 内 | 必须是无副作用纯函数;非法专属配置推入 KIT-BIZ-003 诊断码拦截 | | Tailwind 样式 | 业务组件放 shell.ts 顶部的 *_CLASS 基准类与 *_AREAS 区域闭集(包装件放 candidates 选项,编译期自动并入当前页);基础/数据组件放自己 .ts 顶部的 *_CLASS,紧跟导出 *_CANDIDATES(不拆 classes.ts) | 外部覆写一律走 mergeClass(基准类, 覆写类);基础/数据组件控制器内的类串由应用 components/index.ts 汇总交入 defineKitApp({ appCandidates }) |

与 soma-core 完全对齐的外部应用目录与契约规范

应用工程根目录完全沿用 soma-core Host 标准结构(soma.app.ts / soma.server.ts / config/ / locales/ / pages/ / components/ / domain/ / api/service/);自定义组件直接采用 soma-core 原生 SomaComponentDefinition(App* 前缀、layer: basic | data、snake_case 事件名、data-soma-id 作用域锚点、services 注入白名单):

my-app/
├── soma.app.ts                  # 唯一装配入口(浏览器可进):defineKitApp 挂载组件、packs、actions、appCandidates
├── soma.server.ts               # 可选服务端实现(经惰性 import 交入):身份鉴权、请求钩子、虚拟页
├── kit.app.yaml                 # 应用声明底座:theme 主题、baseComponents / staticObjectComponents 名单、types 覆盖
├── config/                      # 可选:运行期共享配置(如 app.config.yaml)
├── locales/                     # 可选:多语言字典(如 zh-CN.yaml)
├── pages/                       # 页面声明层(与 soma-core / soma-lite 同名):存放 *.kit.yaml
├── domain/                      # 前端领域纯逻辑与具名动作(如 domain/actions.ts,经 defineKitActions 登记)
├── components/                  # 应用自定义与包装组件目录(与全仓 Host 约定同名;少于 4 个可平铺,多则按层分子目录)
│   ├── index.ts                 # 统一导出扩展组件,并汇总 base/ 与 data/(含被包内置件)的 *_CANDIDATES 供 appCandidates 使用
│   ├── base/                    # 应用基础组件(App*,SomaComponentDefinition layer: basic)
│   ├── data/                    # 应用数据组件(App*,SomaComponentDefinition layer: data;一件一个 App*.ts,包装/组合/自绘同此)
│   ├── biz/                     # 应用业务组件外壳(App*,KitBizDefinition;一件一目录 <kebab>/{shell,compile,index}.ts,含包装件、组合件)
│   └── frames/                  # 可选:应用自定义页面框(App*Frame)
└── api/
    └── service/                 # 入站业务服务(URL 段对应目录,_shared/ 存放内部私有共享代码)

若需跨多个应用共享一套扩展组件,按 pack-starter 与 soma-kit/packs/* 同构方式用 defineKitPack 封装为独立包,通过 defineKitApp({ packs: [...] }) 接入。


编辑器补全与诊断定位

*.kit.yaml 是 YAML DSL,没有语言服务;用 JSON Schema 补全键与枚举、悬停看说明。schema 由语法真源 spec/kit-grammar.json 生成:

  • spec/generated/kit-page.schema.json:页面(page / frame / product、对象 schema / behavior / view、字段引用键、collection 键、动作键、各基础组件 props;枚举取自真源闭集,description 取自真源 note)。
  • spec/generated/kit-app.schema.json:kit.app.yaml(键闭集对应 src/app/app-config.ts)。
  • 重新生成:npm run schema:build -w soma-kit;新鲜度门:npm run verify:fresh -w soma-kit(真源改了而生成物没更新即退出码 1)。

VS Code 用法:装 Red Hat 的 YAML 插件,在工作区或用户 settings.json 里映射(路径按你的位置改):

{
  "yaml.schemas": {
    "./soma-kit/spec/generated/kit-page.schema.json": "*.kit.yaml",
    "./soma-kit/spec/generated/kit-app.schema.json": "kit.app.yaml"
  }
}

不想改设置时,在文件首行写相对路径的modeline,示例见 ../soma-kit-demo/:

# yaml-language-server: $schema=../../../../spec/generated/kit-page.schema.json

schema 只做补全与提示,对象内部的字段级语义仍归编译器校验(未列全的位置放宽,不会误报)。

编译诊断(KitDiagnostic)除 code / path 外,应用装配编译页面时还带 file / line / column(1 起);KitAppAssemblyError 的消息按 文件:行:列 [码] 路径:说明 输出。行列由 src/compile/locate.ts 按路径扫描块式 YAML 得到(流式 { } 内部落到所在项的行);定位不到时只有路径。

公开子路径(package.json exports)

| 子路径 | 内容说明 | |---|---| | soma-kit | 包根入口:语法常量与顶层公共类型重导出 | | soma-kit/app | 浏览器可进入的应用装配入口:defineKitApp、defineKitActions、defineKitPack、defineKitBizComponent;包装与组合原语 wrapKitBizComponent、composeKitBizComponents、wrapKitDataComponent、composeKitDataComponents、mapKeys;片段工具函数 emptyFragment、mergeFragments、fragmentFromEmit;嵌入挂载助手 mountKitComponent;通知端口 createKitNotifyRenderer;权限更新 refreshKitPermissionGates、setKitPermissionChecker、权限并集 addKitPermissionChecker;应用叠加 composeKitApps、mergeKitTables、createKitPermissionMiddleware;服务端分页适配 createPagedListAdapter、unwrapBizEnvelope、isBizFailure、withRowId、cursorPageWindow,以及相关类型导出 | | soma-kit/app/server | 服务端装配面(通过 defineKitApp(...).server() 惰性载入,应用运行代码无需直接引入) | | soma-kit/biz | 业务组件框架、13 个内置业务组件(StandardCrud、MasterSubWorkstation 等)、协作数据组件定义与类型 | | soma-kit/frames | 页面框注册表 KIT_FRAMES 与页面框定义类型 KitFrameDefinition | | soma-kit/workspace | 工作台视图与多标签状态机核心纯函数:mountKitWorkspace,支持独立嵌入或受控驱动 | | soma-kit/base | 44 个基础组件的组件字典与样式候选列表(KIT_BASE_CLASS_CANDIDATES) | | soma-kit/base/shared | 基础组件共享工具(str、bool、num、joinClass、尺寸规范等),支持应用自定义基础组件复用 | | soma-kit/base/motion | 动效基础组件字典(TextRotate、Hover3d、HoverGallery、Aura、Marquee)与动效候选类 | | soma-kit/compile | 页面编译器出口(compileKitPage 等),用于构建工具与测试流水线 | | soma-kit/object-renderer | 对象渲染内核(emitObject、renderObject、mergeClass、createKitTableActions 与对象类型定义) | | soma-kit/data | 写死形状数据组件工厂函数与静态注册表(KIT_STATIC_BUILTIN_SPECS、createKitStaticRegistry) | | soma-kit/i18n | 内置文案表与取词(kitText、kitLocaleAt、kitDirAt、kitKey)、$t: 解析(resolveTextRefs、lookupKitText);应用自定义组件用它取页面语言与内置文案 | | soma-kit/packs/commerce | 电商领域包 commercePack:CommerceAddressCard、CommerceOrderCard、CommerceCouponCard、CommercePurchaseOrderCard | | soma-kit/packs/finance | 金融领域包 financePack:FinanceReconcileDesk、FinanceInvoiceSummaryCard、FinanceReconcileCaseCard | | soma-kit/packs/ai | 人工智能平台领域包 aiPack:AiAgentRunCard、AiEvaluationCard、AiModelCard、AiGuardrailPolicyCard、AiModelRoutingCard、AiPromptTemplateCard | | soma-kit/packs/ops | 运维发布领域包 opsPack:OpsChangeWindowCard、OpsDependencyStatusCard、OpsErrorBudgetCard、OpsIntegrationStatusCard、OpsReleaseReadinessCard、OpsRunbookExecutionCard、OpsExecutionTaskRunner | | soma-kit/packs/security | 合规安全领域包 securityPack:SecurityAccessReviewCard、SecurityRiskFindingCard、SecurityFindingCard、SecurityVulnerabilityRemediationCard、SecurityControlEvidenceCard | | soma-kit/theme/kit-theme.css | 唯一的 Tailwind v4 @theme 主题头文件 | | soma-kit/spec/kit-grammar.json | 语法真源规范定义文件 |


表格密度、斑马纹、选中高亮与字段说明

  • collection.density: compact | standard | comfortable(缺省为 standard):只微调单元格上下内边距(py),不改动左右边距;--spacing 随全局宿主密度自适应,单表密度不与全局倍率叠乘。
  • collection.striped: true:斑马纹显示(默认关闭)。
  • 选中行高亮恒开:接通选择路径时,KitTableActions 自动给所在行打上 data-kit-selected(表格采用无角色 div 架构,不使用 aria-selected;已选状态由首列选择框承担),行样式 data-kit-selected:bg-primary-soft 生效,样式优先级压过斑马纹与悬停效果。
  • 行操作与步骤执行:同一表格的所有行操作合并至单个 KitTableActions 实例,内部根据 kit_action 路由分流;confirm: 确认与 permission: 权限直接标注在对应步骤上,由 core 按统一口径在前置阶段拦截,取消或无权限立即阻断执行。
  • fields[].help:表单字段辅助说明(置于输入控件下方、错误提示上方,字号颜色为 text-xs text-fg-muted),仅在编辑态呈现,只读摘要与详情中不展示。
  • 诊断码 KIT-OBJ-009:负责拦截非法密度参数、非布尔斑马纹配置以及非法字段辅助文案,也负责 M 波 G2 的表格新键(见下节)。

表格列模型、工具栏、批量条、合计行与就地编辑(M 波 G2)

示例页 /ref/g2(../soma-kit-demo/pages/ref-g2.kit.yaml):一张 14 列订单宽表。

  • 字段新键:fixed: left | right(须连续地排在两端)、align: start | center | end、ellipsis: true(悬停 title 看全文)、min: 16|20|24|28|32|40|48|56|64|80(间距刻度)、editable: true、columnFilter: true | enum | text。写了 fixed / min、或 toolbar 含 columns,表格进滚动模式(定宽列、sticky 偏移是刻度累加;滚动后固定区边缘出现分隔线与阴影);与 stickyHeader 互斥。
  • 集合新键:toolbar: [reload, density, columns, export](列显隐与行高记在 localStorage,键含页面路径与对象名)、batchActions(勾选后换成批量条,confirm 里 {n} 换成个数,确认在条内就地问;需要 select: multi 与勾选态来源如 StandardCrud)、select: { mode, scope: page | all }、summary: { label, fields: { 字段: sum | avg | min | max | count } }(只算当前页)、edit_mode: cell | row(保存抛 cell_edit,编成 ACTION | data.submit;Enter 保存、Esc 取消,校验沿用字段 rules)。
  • 表头筛选值写进 view 切片,作为请求参数 cf_<字段>(枚举多值逗号连接);服务端按参数过滤。
  • 运行时件:KitTableTools(工具栏 / 批量条 / 列显隐 / 就地编辑 / 滚动边缘 / 省略 title)、KitTableSummary(合计行);固定列是编译期属性,运行时不改固定。treeTable 不支持这些新键(报 KIT-OBJ-009 并忽略)。
  • 规格:prd/强化规划/设计包/soma-kit 方言规格.md 的「表格的附加能力」;闭集与限值:spec/kit-grammar.json#collectionKeys.tableExt;单测:tests/object-renderer/table-ext.test.ts。
  • 多级表头与列宽拖拽(Q 波 G4):collection.columnGroups: [{ title, fields }](单层,组内字段在列序里相邻,同一字段至多属一组;写法不对报 KIT-TBL-001,混固定 / 非固定列报 KIT-TBL-002,该组丢弃);collection.resizable: true(表级,固定列自动跳过)或字段级 fields[].resizable(false 退出,固定列写 true 报 KIT-TBL-004)。两者都视同进入滚动模式;列宽只取 tableExt.scroll.minSteps 的 10 个预置档,拖动与键盘(←/→ 一档、Home / End、Enter / 双击复位)共用 frames/resize-handle.ts,只换 w-N 类、不写 CSSOM,宽度不持久化;列设置「恢复默认」同时复位宽度;导出 CSV 表头写「组标题 / 列名」;固定组整体 sticky;可与 virtual 并用(B1:列类 data-kit-col + w-N 与表头对齐,列宽状态只在 KitTableTools 一份),treeTable / stickyHeader 报 KIT-OBJ-009。示例页 /ref/q/g4-groups;单测 tests/object-renderer/table-groups.test.ts。
  • 展开行子表(Q 波 G5):collection.expand: { list, link }(与 { view: summary } 二选一,并写 / 都不写报 KIT-OBJ-005)。目前只在 StandardCrud 的 entity 列表里可用:子对象写在 objects.overlays.<名>(kind: expand,只读、平铺 table / stack、必须有 behavior.from),不画浮层。展开行主键经 SCOPE | semantic | crud_expand 交协调组件写 focus.expandId,镜像成子对象 from 的 link 参数并编成 require,没展开时缺参 core 不取数;一次只展开一行(手风琴)。滚动模式(固定列 / min / 列设置 / columnGroups / resizable)、非 StandardCrud、子对象不合规一律 KIT-TBL-003 并忽略 expand;treeTable 沿用 KIT-OBJ-010,virtual 报 KIT-TBL-005(展开改变行高,与固定行高窗口冲突,保持拒收;需要行详情请用行操作开详情浮层或分页表格 + expand)。实现 object-renderer/collection-expand.ts;示例页 /ref/q/g5-expand;单测 tests/object-renderer/table-expand-list.test.ts。
  • 块级主题继承(Q 波 G5 修订):产品没写的维度一律继承页面。页面 dark / light + 产品对象没写 mode 时补页面极性对应的昼夜词条;页面 inverse + 产品对象没写 mode 时产品块按反相极性求解(作用域类名带 -inv 后缀)。规则见方言规格 §3.4。

行操作折叠、条件行类、翻页方式与地址参数(M 波 F3)

示例页 /ref/f3、/ref/f3-more、/ref/f3-infinite、/ref/f3-shapes(../soma-kit-demo/pages/ref-f3*.kit.yaml)。

  • 行操作:when(行条件不成立按钮不显示)、more: true(固定进「更多」菜单);行内最多 3 个按钮,超出自动折叠。
  • view.list.rowClassWhen: [{ when, class }]:按行条件加类(仅 table,至多 4 项,类受 KIT-STYLE-001 约束)。
  • collection.page:mode: pager | more | infinite、show_total、layout: simple | pages | full(pages = 上一页 + 页码序列(首尾页、当前页高亮、省略号折叠、方向键 / aria-current)+ 下一页;full 再加页长选择与跳页;Pager 组件同名 prop;示例页 /ref/last-fn2)。
  • 必填星号是标签旁的红色独立子元素(view.form.requiredMark: start | end,不并入标签文本)。
  • behavior.params(引用 $page.params.<名> / $page.query.<名>)、behavior.syncUrl: true;page.params / page.query 声明地址参数。
  • 联动子对象(MasterSub / CategoryCrud / ApprovalDesk)没选中主行时不发请求(core from[].require)。
  • 数据组件:Steps 的 status: error 与 optional、Timeline.align、CodeView 的 stream / follow / max_lines / tree;接入面板(LAST E4):CodeView.variants / variant(页签切换多份文本,事件 variant)、RecordCard 的 facts 修饰 mono / copy / tone(事件 copy)与 data.secret(一次性明文,复制并清除,事件 secret_copied)。
  • 规格:「4.9 行操作、翻页方式与地址参数」。

虚拟滚动(N 波 N3)

示例页 /ref/n3(1 万行表格)、/ref/n3-pick(5000 项 Select / Combobox)、/ref/n3-tree(6252 节点 Tree),数据由服务端生成(../soma-kit-demo/api/service/ref/n3/**)。

  • 引擎走 core 虚拟滚动端口(services.engines.virtual,缺省 @tanstack/virtual-core 由 core 动态加载,kit 不 import)。接线在 src/base/virtual-list.ts;引擎不可用(未装配 / 加载失败)时回退为普通渲染,控制台只提示一次。
  • 表格:collection.virtual: { rowHeight: sm | md | lg, height?: sm | md | lg }。行高必须显式给档位名(不接受像素数字),没写 rowHeight 就不虚拟化;表体换成 KitVirtualRows,保留表头吸顶、固定列、选择与批量条、合计行(粘底)、就地编辑、键盘(跨窗口)、aria-rowcount / aria-rowindex。与 page.mode: infinite 不并用(改按 more 处理并报 KIT-OBJ-009),推荐 page: false 一次给全量。不支持 expand 与字段级 action。
  • 长列表组件:Select(searchable 等 combobox 形态)、Combobox、Tree 用 virtual: true + row_height(Tree 另有 height);带 group 分组时不虚拟化;ItemList 条目变高,本波不做。
  • 密度(K1):虚拟表的 collection.density 与工具栏「行高」映射到行高档位,rowHeight 是标准密度的档位,紧凑低一档、宽松高一档;切换后清行、换估算行高并重新测量窗口。
  • 其它小修(K1):TourGuide 步骤卡贴着高亮目标定位(复用 base/popup 贴边翻转,滚动 / 缩放跟随,无目标 / 贴不下落回底部居中);MediaGrid 新增 columns_by: { md?, xl? } 分档列数;Kanban 新增 wip_mode: hard | soft(soft 抛 wip_exceeded、照常移入);竖向 Range 高度随 size(h-32 / h-40 / h-48)。
  • 规格:「4.10 虚拟滚动」;闭集与像素估算:spec/kit-grammar.json#collectionKeys.virtual;单测:tests/base/virtual-list.test.ts、tests/base/virtual-options.test.ts、tests/data/virtual-rows.test.ts、tests/object-renderer/virtual-table.test.ts。

右键菜单与 CSSOM 几何写入门(Q 波 G6)

  • contextMenu(view.list 或 collection,仅 table / cards / stack):contextMenu: ["@detail", "@ship", "@remove"],元素是 behavior.actions 的动作 id,至多 8 项(collectionKeys.contextMenu.maxItems)。鼠标右键 / 触屏长按 / Shift+F10·ContextMenu 键打开,Esc 关闭并回焦该行;可编辑输入控件内部保留系统菜单;右键不改勾选。条件显隐 when、permission、危险项红字、confirm 尾段都沿用行动作。菜单项抛 row_action(载荷与 KitTableActions 同形,带整行),页面 _event 与行操作共用同一套步骤。写法不对报 KIT-MENU-001。
  • 实现:写死数据组件 KitContextMenu(src/data/context-menu.ts),菜单内容复用 menu-core 的 wireMenu,按落点经浮层端口挂出(portal-core 的 showAtPoint,翻转 / 夹取复用 popup.ts 的 resolvePopPlace)。MenuButton 的 trigger: contextmenu 复用同一份 showAtPoint;浮层端口缺失时降级为就地绝对定位(不写 CSSOM)。
  • CSSOM 例外总表:运行期 .style.x = 只许出现在 spec/kit-grammar.json#cssomWrites 登记的函数里(E1 virtual-list.ts 的 setSpacerHeight 写 height;E2 portal-core.ts 的 placeAtPoint 写 left / top)。npm run verify:cssom -w soma-kit 判定,越权或登记项陈旧都红;已登记在 scripts/test-all.mjs 的静态门阶段 kit-cssom,负样本见 tests/spec/cssom-gate.test.ts。新增写入必须先改 cssomWrites 与《方言规格》§7.7。

声明式请求 / 下载动作与服务端分页适配(最后一轮 E1)

动作写 request 或 download,不必为「打一个接口」写具名动作(真源 kit-grammar.json#requestAction,规格 3.3.1):

actions:
  - id: toggle
    name: 启停
    on: row
    confirm: 确认切换?
    request: { api: /api/x/toggle, body: { id: $row.id }, ok: { path: success, equals: true, message: message }, notify: 已更新, into: result }
  - { id: export, name: 导出, on: toolbar, download: { api: /api/x/export, query: { kw: $filter.name }, filename: x.csv } }
  • 取值路径:$row.* / $focus.*(浮层里取被点的行)/ $filter.* / $page.page|size / $value / $selected.字段(批量,batchActions: ["@id"]);其余是字面量。
  • request 经 core 写通道(合成只写对象 + data.submit)发 POST / PUT / PATCH,结果落 write-outcome-store;into 是结果范围名,消费方用 soma-kit/data 的 watchKitOutcome(services.data, 名字, cb) 读 { status, message, data, seq }。失败弹提示,成功弹 notify 并重取(then: none 不重取)。
  • download 是浏览器原生下载(GET 链接 / POST 表单),不走 fetch。
  • 页面里有这类动作时,编译器补页面级 KitActionHost(不需要应用登记任何东西)。AI 不能写这两个键。
  • 附加浮层保存:events.saved: { action: <request 动作 id>, then: saved },请求成功才关窗。
  • 服务端薄壳:soma-kit/app 的 createPagedListAdapter(页码协议 <-> 旧后台分页与信封)、unwrapBizEnvelope、cursorPageWindow(游标分页)。

编辑工作台:辅助栏 / 子表字段 / 级联 / 字段策略表(最后一轮 E3)

示例页 /ref/last/e3(ref-last-e3.kit.yaml),规格「4.6.1 / 4.6.2」,真源 kit-grammar.json#rowsField / formLinkage.optionsFrom / fieldPolicy:

  • MasterSub 辅助栏:product.objects 多一个可选的 aside(单个对象,第三栏 4 / 5 / 3)。写 behavior.from 按选中行取数;或 view.summary.staticComponent: AsyncResultPanel + props.outcome: <request 动作的 into> 展示动作结果(不需要 from)。
  • 表单子表字段:schema 里 type: text + edit: RowEditor,列与行为写在 form 视图字段项的 props(columns / edit_mode: inline / fixed_rows / min_rows / max_rows / footer);值是对象数组,随表单提交,列校验与行数上下限在提交时逐行判(第 N 行:…)。fixed_rows 用于「字段策略」这类只勾选不增删的表。
  • 级联字段:多级依赖下拉用 options.from 的 params: { x: { field: 上一级 } }(既有);一条路径用 edit: Cascader,options 加 lazy: <参数名> + levels: <层数> 按层懒加载(或行里带 children 一次给整棵树,leafKey 标叶子)。
  • 字段策略表:view.form.policy: { by: 状态字段, table: { 取值: { hide / readonly / required: [字段] } } },编译成 showWhen / disabledWhen / requiredWhen,不新开运行时。
  • AsyncResultPanel.outcome / show_run:订阅请求动作的 into 结果(进行中 / 成功 / 失败),纯展示时 show_prompt: false + show_run: false。
  • 单测:tests/object-renderer/field-policy.test.ts、tests/data/rows-field.test.ts、tests/biz/master-sub/master-sub-aside.test.ts。

表单草稿 / 编辑详情 / master 筛选(最后一轮 W1)

  • $draft(request 取表单草稿):body 里写 $draft.字段,把编辑框里还没保存的值发给接口(「验证」「试运行」);$draft 整体取全部草稿。草稿仍是 KitObjectForm 自己的,表单挂载时登记读口(soma-kit/data 的 registerKitFormDraft / readKitFormDraft / activeKitFormDraft);多个可见表单取「最近动过」的,没有可见表单读作 {}。具名动作用 kitActionExtras().draft(scope?)。真源 kit-grammar.json#formDraft,规格 3.3.1。
  • view.form.footerActions: [动作id]:编辑弹窗底栏的额外按钮(触发 request / download 动作,文案取动作 name),是 $draft 的触发点;表单抛 footer_action { id }。
  • behavior.detail:编辑弹窗打开时按选中行先取详情,detail: { from: { api, params: { id: $row.id } }, unwrap? }。取数中只显示「处理中…」,成功后详情盖行作表单初值,失败照实报错并给重试、不渲染表单。写法不对 KIT-DET-001。真源 kit-grammar.json#formDetail,规格 4.6.1a。
  • master 的 filter 视图:MasterSubWorkstation 的 master、CategoryCrud 的 category 可写 view.filter,提交 / 重置写 filter 切片并重取该列表(语义同 StandardCrud)。
  • 示范页 /ref/last/w1(ref-last-w1.kit.yaml)。

展示与状态(最后一轮 GC)

示例页 /ref/last/gc,规格「4.6.4」,真源 kit-grammar.json#staticProps.show(数字在 kit-limits#dataLimits),诊断 KIT-SHOW-001 到 KIT-SHOW-005。

  • CodeView variants_from: <路径>:页签来自数据,每个页签单独复制(与 variants 二选一)。
  • RecordCard outcome + secret_path:一次性明文取自某个 request 动作(into)的成功响应,进行中 / 失败不显示,复制后组件不再持有明文。
  • RowEditor staged: true:集合本地草稿。增删改只在本页累积,常驻「未同步 N 项」条与行小标;「同步」抛 submit(走 core 写通道 data.submit,成功才清),「放弃变更」自绘确认后恢复服务端数据;不是乐观更新。
  • 版本锁:behavior.write.version: <字段>(core 的 write.version)与请求 body 的 $loaded.<对象>.<字段路径>;缺值不发请求,冲突如实报「已被他人修改」。
  • FlowEditor adopt: { from, path? }:画布建议来自请求结果,画成虚影,点「采纳」才合入草稿。

表单与字段(最后一轮 GB)

示例页 /ref/last/gb/steps(跨步取值)、/ref/last/gb/form(初值 / 隐藏 / 派生)、/ref/last/gb/flow(画布字段 / 子表远程选项),规格「4.6.5」,真源 kit-grammar.json#formLinkage.fieldValue / flowField(数字在 kit-limits#formLinkage.fieldValue),诊断 KIT-FORM-010 到 KIT-FORM-012、KIT-STEP-005。schema.<字段> 新增 default / hidden / derive 三个键(human-only,AI 写面不可写)。

  • default(字段初值):字面量,或 $filter.<字段> / $row.<字段> / $steps.<步骤名>.<字段>。只在新建时填「空且用户没改过」的字段;编辑态不生效,取不到值不填。
  • hidden: true(隐藏草稿字段):随草稿提交、表单不画。不是安全边界、不能绕过权限或校验:必须是可提交字段(w / create,r 报错),create 字段编辑态仍不提交,字典 rules 照常校验(不过时提示条写明字段名与原因),不被采纳建议回填;服务端必须自行校验。
  • edit: FlowEditor(画布作表单字段):值 { nodes, edges, mappings? } 随表单提交;必填时至少一个节点;FlowEditor 新增 value / name / id / disabled 四个 prop,change 载荷多一个 value。
  • RowEditor 列的远程选项:columns[].options: { from, valueKey?, labelKey?, rowsPath?, params? }(kind: select);取数中 / 失败都如实显示,不假装有选项。
  • derive: { template }(派生展示字段):permission: r,闭集模板 {字段名|过滤器},过滤器 json / upper / lower / trim / count / or:兜底;没有表达式引擎(无运算、比较、函数调用、嵌套)。只在 form 视图显示,不进提交、不参与校验。
  • StepForm $steps:default、options.params({ steps: '步骤名.字段' })、derive.template 的 {steps.步骤名.字段}、步骤 request 的 body,都能取更早步骤已确认的值(闸门早已支持)。

动作与结果(最后一轮 GA)

示例页 /ref/last/ga1、/ref/last/ga2,规格「4.6.6」,真源 kit-grammar.json#formLinkage.requestExt(数字 kit-limits#formLinkage.requestExt,超时区间 core write.timeoutMs),诊断 KIT-ACT-003…007 / KIT-RES-001。全部 human-only,AI 不可写。

  • 组件事件接动作(GA1):view.on: { run: probe },probe 是 behavior.actions 里的 request / download 动作 id;组件事件的载荷作 $event 取值根($event.method、$event.params)。
  • 动作按状态禁用 / 隐藏(GA2):actions[].disabledWhen / hiddenWhen,谓词叶子是 outcome.<into>.status|message|data.路径 或行 / 草稿字段(工具栏动作读 outcome.* 与页面读态 total / selectedCount / selected.<字段>)。request.into 可写 { name, key: $row.<主键> } 让结果按行分槽,行内动作默认读本行那一份;RecordCard secret 复制后经 services.data.clearOutcome 清掉 core 存储里的明文。行操作、右键菜单、工具栏按钮、表单底栏动作都认;随写入结果自动重判。
  • 请求结果常驻(GA3):view.form.result: <into>(表单 / 弹窗内常驻,进行中 / 失败 / 成功都留在原地);非 form 视图写 result 编成只展示结果的 AsyncResultPanel 区域。
  • 请求前输入(GA4):request.prompt: { title?, fields: [{ name, type?, default?, required?, options? }] },先弹自绘输入框,输入值作 $prompt.<name>;取消不发请求。
  • 新建预填(GA5):builtin: openCreate 的动作写 prefill: { 字段: $filter.x | 字面量 },同页可有多个预设不同的「新建」钮(StandardCrud;MasterSub / CategoryCrud 同样支持,H1a,联动型预填只许字面量)。
  • 请求超时(GA6):request.timeoutMs 编进合成写对象的 core write.timeoutMs(1000..600000 毫秒,core 经 http 请求配置 timeout 下发)。

动作与刷新补充(H1)

示例页 /ref/last/h1a、/ref/last/h1,规格「4.6.6」H1 段,真源 kit-grammar.json#requestAction(防抖毫秒 kit-limits#formLinkage.requestExt.changeRequestDebounceMs),诊断 KIT-ACT-007…009。全部 human-only,AI 不可写。

  • H1a 联动组件预设新建:MasterSub 主对象 / CategoryCrud 明细对象的工具栏可并列多个 builtin: openCreate + prefill: { type: http } 的钮;只许字面量。
  • H1c 后续链数组:request.then: [{ reload: true | 数据对象名 | /接口路径, on?: success | failed | always }];on: failed 即「失败也执行」(失败后刷新列表);写成数组后缺省刷新不再发生。
  • H1b 跨页签重取:reload: <数据对象名>(StandardCrud 主对象 = <页签 id>_entity)点名任意页签里的对象;reload: /api/x/options 让页面里所有 options.from 等于它的字段重取候选(含其它页签的表单)。点名不存在的对象 / 路径编译报 KIT-ACT-009。
  • H1d 挂载自动发只读请求:request: { read: true, on: mount },页面挂载时宿主自动发一次(进页就跑前置检查,下载钮的 disabledWhen 进页即正确);写类请求不得自动发(没声明 read: true、非 POST、带 prompt / confirm / permission / 取 $row $draft 等挂载时没有的值,均报 KIT-ACT-008)。read: true 的请求 then 缺省为 none。
  • H1e 字段变化触发请求:view.form.onChange: { table: loadMeta },用户改了该字段后防抖发 loadMeta(须 read: true),body 用 $event.value / $draft.x;在途时只留最新一次,被顶替那次的结果丢弃(网络请求本身不取消,core 写通道没有中断接口)。

采纳建议回填草稿 / 向导阶段闸门(最后一轮 W2)

示例页 /ref/last/w2(采纳)与 /ref/last/w2/gate(闸门),规格「4.6.3 / 5.3.1」,真源 kit-grammar.json#formLinkage.adopt / formLinkage.stepGate,诊断 KIT-FORM-009 / KIT-STEP-004。

  • view.form.adopt: { from, path?, map?, label?, ask?, askLabel? }:某个 request 动作(into: 名字)返回一组建议值,用户在表单顶部的采纳条上点「采纳」才把它们写进当前草稿。只改草稿,不提交,保存仍由用户确认。被改的字段标签旁出现「已采纳」(只表示「当前值就是被采纳的建议值」,再改、重置、保存成功即消失)。from = request 的 into;path = 建议对象在响应体里的点路径(接口带信封时写 data.xxx);map = { 表单字段: 建议路径 },缺省按同名键回填;ask = 采纳条上「获取建议」钮要发的 request 动作 id(本对象 behavior.actions 里不写 on,其 into 必须等于 from,body 可取 $draft.*)。不可回填的字段(只读、被条件隐藏或禁用)不进「N 项可采纳」,条上照实写「另有 M 项不可回填」。「审计网关」之类领域流程不进 kit,应用照常把请求发到自己的接口。
  • steps.<名>.gate: { when, message, run?, runLabel? }(StepForm):进入下一步(末步是提交)的前置条件。when 是闭集谓词(present / blank / 算子,all / any 一层),叶子可引用本步字段、outcome.<into>.status | message | data.<路径>、steps.<更早步骤>.<字段>;不满足时按钮禁用并常驻显示 message(message 必填,回车提交同样被拦);run 指向本步不写 on 的 request 动作,被拦住时提示旁出现「去验证」钮。步骤的 behavior.actions 只许这种动作。注意业务信封 success: false 时 outcome.<名>.status 仍是 saved,「验证通过」请写 outcome.<名>.data.success eq true。
  • 登记的草稿读口新增实现 patch(values)(与采纳条共用回填逻辑)。两项均 human-only,AI 不可写。
  • 单测:tests/object-renderer/adopt-gate.test.ts、tests/object-renderer/adopt-render.test.ts、tests/data/object-form-adopt-gate.test.ts、tests/biz/step-form/step-form-gate.test.ts。

国际化与 RTL(N 波 N5)

示例页 /ref/n5/zh、/ref/n5/en(同一页面两种语言)、/ref/n5/rtl + /ref/n5/rtl-body(dir=rtl 阿拉伯语工作台)。

  • 页面语言与方向:page.locale(缺省应用 defaultLocale)写进页面根 <div data-soma-host … lang dir>;page.dir: ltr | rtl 缺省按语言推导(真源 spec/kit-grammar.json#i18n.rtlLangs)。两者都没声明时根不写 lang / dir。
  • 内置文案:src/i18n/kit-text.ts 一张「键 → { zh-CN, en }」表(键 soma.kit.*)。组件运行期 kitText(键, 参数, 元素) 读元素最近的 [lang] 祖先(不退回 <html lang>,Next 宿主写死 en);业务组件 / 页面框编译期 kitText(键, 参数, ctx.locale)。没声明语言取 zh-CN,词表没有的语言(如 ja、ar)取 en;显式 prop(cancel_text / empty_text …)永远优先,这类 prop 缺省值改成空串,由内置文案补。
  • 页面词条 $t:键:值位置的 $t:键 在 kit 编译期按页面语言展开(页面顶层 locales: { 语言: { 键: 文案 } } → 应用词条表 KitAppContext.locales → 内置词表;先试完整标签再试主标签,再换应用缺省语言,最后 en),core 只收到展开后的文案;找不到报 KIT-LOCALE-001。应用 locales/*.yaml 的读取与交入由 Host 装配负责(见 N5 报告)。
  • RTL:内置组件的物理方向类已换成逻辑类(ms-/me-/ps-/pe-/start-/end-/text-start/text-end/border-s/border-e/rounded-s/rounded-e/rounded-es/rounded-ee),带符号位移配 rtl: 变体;浮层贴边翻转按方向换算(resolvePopPlace(…, rtl));手写方向键处理经 kitKey(key, 元素) 在 RTL 下互换左右键,core 接线的集合宿主读最近 [dir] 自动换向;侧栏 / 分栏拖柄的指针换算按方向。物理角命名的浮层(ContextMenu 的 tl/tr/bl/br、Fab 的 bottom-right)不翻转。
  • 带参词条(B1 A):{ $t: 键, args: { 占位名: 值 } },词条原文里的 {占位名} 按 args 替换(口径同 core 的 interpolate,不另写一份)。静态值(字符串 / 数字 / 布尔,不许含花括号)编译期展开;@row.字段 改写成行占位 {字段} 留在文案里,由运行期持有行的组件插值(目前只有删除确认,KitCrudBridge 按被删行插值,只进确认弹窗的纯文本)。字符串形 $t:键 不带参。删除确认缺省文案:对象有标题字段(title / name / label 取第一个)时为「确定删除「{标题}」?」,没有则不带名字;确认文案里写的 {name} 指 name 字段,没有 name 字段时指标题字段;占位不是 schema 字段报 KIT-ACT-001。AI 的 confirm_text 同样可写 {name}。
  • StepForm 放弃时删半成品(B1 A):config.abandon: keep|delete,缺省 keep,只在 saveMode: perStep 下有意义。delete 时每步多一个「取消」钮,首步对象必须声明 write.delete(占位恰是 {stepKey},否则 KIT-STEP-003);已保存过(有主键)才二次确认并删除,没保存过直接回初始;离开页面(站内链接)同样守卫;删除失败照实 toast,不拦离开;刷新 / 关闭标签只有浏览器自带提示,来不及异步删除。
  • 应用语言与词条表:defineKitApp({ defaultLocale }) 与应用根 locales/<语言>.yaml(平铺 键: 文案)由 assembleKitServer 读成 KitAppContext.defaultLocale / locales;没有 locales/ 目录就没有应用词条表。
  • 规格:「4.11 国际化与 RTL」;单测:tests/i18n/kit-text.test.ts、tests/i18n/resolve-text-refs.test.ts、tests/base/n5-rtl.test.ts。

声明组件嵌入了哪些内置组件(declareKitUses,N 波 N8)

样式按页裁剪后,应用组件在运行期命令式嵌入的内置组件(mountKitComponent 等)编译器看不到,需要声明:

import { declareKitUses } from 'soma-kit/app';
export const AppDeployPanel = declareKitUses(def, ['Button', 'Input', 'Alert'], DEPLOY_PANEL_CLASS_CANDIDATES.split(' '));
  • declareKitUses(def, uses, own?):uses 为嵌入的内置组件契约名(及其闭包类一并并入);own 为本组件自己写死的类,页面用到它才并入,不再放全局 appCandidates。返回同一个定义,可与 wrapKitDataComponent / composeKitDataComponents 叠加。
  • 未声明的 App* 组件:样式退到全部基础件类兜底,并整进程按组件名提示一次 KIT-STYLE-003(console.warn,不阻断)。
  • 运行期开发态提示:挂载了未被任何声明覆盖的非常驻内置组件时 console 提示一次(生产不提示;进程里没有任何声明时不提示)。
  • 领域包用 defineKitPack({ componentCandidates }) 按包内数据组件登记候选类,不再有整包 candidates。
  • 虚拟滚动的占位高度是 kit 里唯一的运行期几何写入(CSSOM el.style.height,只在 setSpacerHeight),边界见规格 §4.10;仓库内联样式相关门不判它,无需豁免。
  • 规格:「4.12 嵌入声明与按页样式裁剪」;指南第 6.4 节;单测:tests/compile/declare-uses.test.ts。

主题机制(kit.app.yaml 的 theme)

theme:
  palette_scheme: soma-finance-steel   # 配色方案(定义于 soma-design/spec/palette-library.json)
  font_scheme: auto                    # 字体方案(font-library.json)
  color: '#336699'                     # 可选:品牌主色号
  mode: system                         # 模式:light / dark / system / soft;缺省为 system

装配期(soma-kit/app/server)将 theme 传入 soma-foundation/compile 的主题求解流水线,统一计算出亮色与暗色两套 --soma-* 令牌值,并序列化为 :root 与暗色样式块,拼装在主题头前提交样式引擎。应用代码严禁手写固定色值。 暗色切换跟随系统设备偏好或通过根节点 soma-scheme-dark / soma-scheme-light 样式类即时切换,无须重新编译。取值越界报 KIT-APP-002,求解失败报 KIT-APP-003,直接阻断构建。

块级主题(page.theme / product.theme)

页面与产品可各写一个 theme,只改所在内容块的光学令牌(page.theme 只管内容区,不含页面框外壳;整站主题仍只在 kit.app.yaml):

page:
  theme: dark                                  # 简写 inverse / dark / light
product:
  theme: { mode: 深邃暗夜, color: 科技海蓝 }     # 或词条对象;只有 mode / color 两个键

只收词条,不收 hex、factors、name、别的维度键;越界报 KIT-THEME-001(形状)/ 002(未知词)/ 003(越权或区域不可用)。令牌在编译期由 foundation 求解为 .soma-scope-<id> 变量块,作用域类由编译器写在产品根的包装上;产品块覆盖页面块(两者都是对象时只覆盖自己写的键)。校验入口 checkBlockTheme(soma-kit/src/theme/block-theme.ts),键表与回喂模板在 spec/kit-grammar.json#blockTheme。演示页 /ref/q/theme-product、/ref/q/theme-page。


装配项透传与通知

在 defineKitApp({ auth, eventMiddleware, renderPorts, services, identity, ... }) 中:

  • auth、eventMiddleware、renderPorts 透传给前端浏览器引导程序;服务端项交由宿主装配;
  • renderPorts 默认提供 { notify: createKitNotifyRenderer() }(采用 Tailwind 语义色的轻量级提示弹条;确认操作唤起浏览器原生对话框);
  • 具备服务端实现的私有服务目录(以 _ 开头的目录或文件)只用于模块间共享,不向路由暴露。
  • startup:服务启动钩子,soma dev / soma start 的服务进程启动时(Next instrumentation.ts 的 register)在装配完成后执行一次,抛错即拒绝启动;soma build 不执行。soma.app.ts 能进浏览器包,服务端实现惰性导入:startup: async () => (await import('./soma.server')).startup()。
  • responseHeaders({ pathname, method, kind }):给页面(kind: 'page')、/api/*('api')、静态资源('asset')的响应统一加头,同名以此为准,可同步或异步,返回 undefined 表示不加;例如给非图片请求加 Cache-Control: no-store。composeKitApps 对 startup 顺序执行、对 responseHeaders 合并(叠加应用覆盖基座同名头)。详见 docs/03-类与模块/soma-core类与模块设计.md。

领域包规范(Packs)

携带行业或特定业务名词的专用写死形状数据组件与业务组件放置在 soma-kit/packs/<领域>/ 目录下,由应用根据业务场景显式引入:

import { defineKitApp } from 'soma-kit/app';
import { commercePack } from 'soma-kit/packs/commerce';
import { financePack } from 'soma-kit/packs/finance';

export default defineKitApp({ packs: [commercePack, financePack] });
  • 领域包使用 defineKitPack({ name, staticComponents, bizComponents, dataComponents, candidates }) 统一构建;
  • 领域包组件必须在内置通用组件基础上包装一层实现,禁止脱离底层组件重写基础交互;
  • 包内组件命名必须以大写首字母的包名作为前缀(如 CommerceAddressCard);命名冲突直接抛错 KIT-PACK-002,禁止静默覆盖;
  • 各领域包覆盖范围:
    • commerce(电商):四类卡片封装自 RecordCard,支持传入金额格式与选择动作;
    • finance(金融):包含对账核销业务台 FinanceReconcileDesk、发票摘要卡 FinanceInvoiceSummaryCard 等,金额数值严格由后端负责,组件不私自运算;
    • ai(人工智能):封装 RecordCard、StatGroup、Checklist 等,用于展示智能体运行步骤、模型评测、策略路由与提示词模版;
    • ops(运维发布):用于变更窗口、依赖状态、错误预算监控、发布就绪度与发布流程审批;
    • security(合规安全):用于访问权限复核、安全漏洞处置卡与合规证据清单。

额外页面目录与默认主题

  • defineKitApp({ extraPagesDirs: ['../shell/pages'] }):支持指定额外页面扫描路径,与应用主路径页面共同编译;若存在跨目录的重复 URL,立即抛出 KIT-PAGE-004 诊断;
  • defineKitApp({ defaultTheme: { paletteScheme, fontScheme, color, mode } }):提供应用级兜底主题,当配置文件未显式指定主题时生效。

服务目录与浏览器端边界

  • soma.app.ts 引入 soma-kit/app,仅包含可安全进入浏览器环境的代码;
  • 服务端专用逻辑与 Node 依赖通过动态导入按需加载,并由打包配置隔离,绝对不会泄露至前端发布产物。

AI 页目标(soma-kit/page-target)

细节见 docs/03-类与模块/soma-kit类与模块设计.md 与 docs/04-语法规范/Kit套件语法规范.md,摘要:

soma-kit 实现 AI 生成页的 kit 目标:createKitPageTarget(app) 返回 soma-core/page-target 的 PageTarget(format 为 kit,owner 为 soma-kit,读 design page-origin.json)。soma-ai-page 只经接口使用它,不 import soma-kit。仅服务端 / 测试环境可达,浏览器入口不得可达;目标目录不 import ai / zod / soma-ai* / next / react(AIP-G16)。

  • 蓝图:模型只出意图蓝图(kitIntentSchema()):title、subtitle?、locale?、页面框意图、页面模式意图、objects(字段 name / label / type / key / required / options,接口 api / write_api 只能取授权接口,sample?)、roles、views(角色的槽位放哪些字段)、actions(内置动作)、tabs?。
  • 意图词:页面模式与页面框用抽象意图词(页面模式 crud / master-detail / category / board / form / detail / steps / approval / settings / import / result,页面框 plain / blank / top-nav / tabs),由 kit 目标按 kit-grammar.json#aiIntentMap 确定性映射到 bizComponents / frameKinds 闭集;映射目标 ⊆ 闭集,由真源自检(soma-kit/tests/spec/ai-writer.test.ts)。AI 不写组件名。
  • 写面:kit-grammar.json#aiWriter 是写面键表(ai | compiler | human-only,表外键一律 human-only),生成投影 KIT_AI_WRITER(src/generated/kit-grammar-runtime.ts),目标代码只读投影。样式类、视觉数字、显式组件、颜色、frame.config / product.config、事件与联动胶水、page.url 等越权键拒收(KIT-AIT-002)。
  • 开放面:块级 theme(蓝图根 = page.theme,top / tabs[i] = 产品块;只收词条,同一个 checkBlockTheme,整站主题仍只在 kit.app.yaml);字段展示语义词 display(kit-grammar.json#aiDisplayMap,只作用于 list / summary,类型不符 KIT-AIT-005,component / props 仍禁);内置动作 openCreate / openEdit / reload / remove(remove 仅 crud 行动作,对象另给授权的写类 delete_api,编译器恒追加 confirm 与 tone: danger,缺确认或删除通道 KIT-ACT-001,人写页同守)。
  • 管线:accept(形状 + 闭集 + 写面 + 授权接口核对 + 确定性编译为 page.kit.yaml,同一蓝图逐字节相同)→ dryRun(compileKitPage + 写面再检 + 标准载荷)→ 零 Domain 冒烟 → submit。drift 为整页重编后按 revise_scope 对账。
  • 提交面:soma-kit/app/server 的 pages.submit / unbind / list(createKitServerPages):定址、pageGrants 授权终裁、信封校验、编译、registerSomaPage(同 URL 覆盖,乐观并发 expectGeneration);溯源 provenance 必填;persist 只取 runtime / memory,registry 拒收(KIT-AIT-006)。
  • 诊断码:KIT-AIT-001…006(形状、越权、未知键或闭集外取值、越出授权接口、对象描述不足、提交面拒收;另 KIT-ACT-001 删除缺确认),登记在 kit-grammar.json#diagnostics;编译层沿用 KIT-PAGE-* / KIT-OBJ-* / KIT-FRAME-* / KIT-BIZ-* / KIT-TYPE-*。
  • 判定落点:AIP-G06 / G13 / G15 / G16 / G17 / G18,见 docs/03-类与模块/soma-ai与ai-page类设计.md;测试在 soma-kit/tests/page-target/ 与 soma-kit/tests/app/server-pages.test.ts。

字段与派生补齐(最后一轮 H2)

示例页 /ref/last/h2/flow(H2a / H2b)、/ref/last/h2/adopt(H2c)、/ref/last/h2/console(H2d),规格「4.6.3 / 4.6.5 / 5.x RequestConsole」,真源 kit-grammar.json#formLinkage.{fieldValue,flowOptions,adopt} 与 #staticProps.show.requestConsole。

  • 画布节点动态选项(H2a):画布字段 props.nodeTypes.<类型>.fields[] 里 kind: select 的 options 写成对象 { from, params?, valueKey?, labelKey?, rowsPath?, meta? },由表单取数(画布不发请求)。params 可取表单草稿 { field: 另一字段 },依赖值变了重取并作废上一次;meta: { 节点键: 行键 } 把所选行的列并入节点 meta;加载中 / 失败如实占位。限值读 kit-limits#formLinkage.flowOptions。错误 KIT-FORM-012。
  • 派生遍历行表(H2b):derive.template 里 {#each 字段}…{/each}(至多两块、不嵌套),块内 {row.键} / {@n};字段值不是数组渲染 0 行,仍然没有表达式引擎。例:'{#each deps}{@n}. {row.alias} -> {row.api}; {/each}'。错误 KIT-FORM-011。
  • 采纳回填隐藏字段(H2c):adopt.map 逐字点名的 hidden 字段可被回填(本来就随草稿提交、非只读),采纳条写明「含 N 个隐藏字段」;不点名的隐藏字段不会被隐式回填,别的组件经登记表 patch 回填也不放开。
  • RequestConsole targets_from(H2d):{ path?, value?, label?, outcome? },目标下拉取自组件 data,或某个 request 动作(into)的成功响应体;加载中 / 失败 / 无行各有占位;与静态 targets 不能同写。错误 KIT-SHOW-006。
  • 单测:tests/object-renderer/h2-fields.test.ts。

业务信封约定(envelope)

后端响应的「业务失败」口径只声明一次:kit.app.yaml 的 envelope: { failWhen, messagePath, dataPath }(页面 page.envelope 可覆写,human-only)。 failWhen 是 core 的闭集谓词({field, op, value} 与 all_of / any_of),命中即业务失败;messagePath 是失败文案位置;dataPath 是成功数据位置。 编译器把它编进 core 数据声明的 write.failWhen / messagePath(以及 from[]):HTTP 200 + 命中也记 failed,闸门可写 outcome.x.status eq saved。 详情预取(behavior.detail)、服务端分页适配(unwrapBizEnvelope / createPagedListAdapter)、具名动作 http(kitActionHttp / createKitActionHttp(envelope))读同一份;kit 与 core 都没有写死的 success / ret / data。 闸门引用的 outcome 来自读了 $draft 的请求时,被读字段之后一改,结果即过期,闸门重新拦住并提示「内容已修改,请重新验证」。