Skip to content

功能规格说明:创作集(Artifacts 画廊) ​

创建日期:2026-10-20

需求来源:原型与需求文档 v1.19(https://codechat.codewave.163.com/code/artifact/4ybm36o8s4)

本期范围:需求文档第一、二、三、六节(视图与分类 / 模板新建 / 列表搜索 / 用户场景识别)。 非目标(本期明确不做):第四节「评论」的全部交互(列表操作列的评论按钮、评论列表、逐级回复、AI 是否解决标记、一键拉取评论命令),第五节「订阅」(在对话里描述订阅、由 AI 完成订阅)。 页面归属:创作集是账号级整页(内容与当前会话、工作目录、分屏数量无关),形态复用桌面端「插件市场」:保留左侧栏、替换 pane 行、自带返回;不做成右侧面板的 tab,桌面端独有(VS Code / JetBrains 不提供)。 数据面:全部走 codechat 的 artifact 接口(GET /api/frame/frames、POST /api/frame/{slug}/share、GET /api/frame/{slug}/versions、DELETE /api/frame/{slug})。服务端需要补齐的字段与接口集中列在「外部依赖与服务端补齐」一节,不在各用户故事里重复。

用户场景与测试 ​

用户故事:创作集入口与「我发布的 / 共享给我的」列表(优先级:P1) ​

作为桌面端用户,我希望从左侧栏直接进入创作集、看到我发布的和别人分享给我的创作(文件名称、发布人、最近发布时间、分享给),以便不用记得链接就能找回并分享自己做过的页面。

为什么是这个优先级:创作集是 Artifact 能力的「收纳面」——发布出去的页面现在只有拿到链接才能回到,列表是所有其它操作(分享、撤销、归类、打开对话)的唯一入口。

独立测试:已登录账号下发布两个 artifact(其中一个分享给两位成员、一个仅自己)并由另一位成员分享一个过来,打开创作集,验证默认停在「默认视图 · 我发布的」,列齐全、按最近发布时间倒序、自有行的「分享给」显示真实范围(含具体账号)、共享行的「分享给」为 —;断开网络重进页面验证错误态与重试;未登录进入验证提示先登录。

验收场景:

  1. 假设应用以 desktop 宿主运行且左侧栏展开,当用户点击侧栏的「创作集」入口,则主区整页替换为创作集(侧栏保留、pane 行与当前对话不销毁),入口在页面打开期间显示选中态,点击页面左上角返回箭头回到进入前的会话视图。
  2. 假设创作集首次打开,当数据返回,则默认停在默认视图、分组为「我发布的」,列表列固定为:文件名称、发布人、最近发布时间、分享给、操作;按最近发布时间倒序(同时间按文件名稳定排序),不出现勾选列。
  3. 假设当前账号发布过 3 个 artifact 且被 2 位成员分享过,当切换到「共享给我的」分组,则只列出 rel = shared 的行;这类行的「发布人」显示分享者的账号、「分享给」显示 —(范围由对方管理),操作列只有「复制链接」「在浏览器中打开」(自己没有编辑权)。
  4. 假设「我发布的」里三条行的分享范围分别是仅自己、共享给整个企业(服务端 shareMode = org)、共享给三位指定成员,当列表渲染,则「分享给」列分别显示固定文案「仅自己」/「企业成员」/「团队成员」加最多两位账号(余下省略,完整名单见 title 与分享弹层,按服务端顺序排列);企业成员档不列任何人名、不带人数(组织内成员会变,列名单无意义),仅自己与企业成员两档的 title 与文案一致;任何行都不显示空白单元格,共享给我的行同列显示 —。
  5. 假设列表为空(当前分组下没有任何创作),当页面渲染完成,则显示明确的空态文案(如「还没有创作」/「还没有人分享给你」),不显示空表格骨架、不报错。
  6. 假设会话未登录或登录态已失效(列表接口 401),当打开创作集,则页面显示「请先登录」的提示与登录入口,不显示空列表(把鉴权失败误呈现为「没有数据」是禁止的);服务端回 403(已登录但无权访问)时不得冒充登录失效,按通用错误提示与「重试」呈现。
  7. 假设列表接口网络失败或返回 5xx,当请求失败,则页面显示错误提示与「重试」按钮,点击重试重新拉取;已渲染的数据不被清空。
  8. 假设创作集打开期间用户在本机发布了新 artifact 或撤销了发布,当回到创作集(或点击刷新),则列表反映最新状态;页面不做后台轮询,也不在打开期间自动变化。
  9. 假设创作集打开期间用户切换会话或新增分屏,当视图变化,则创作集保持打开、内容不重新拉取(切会话不重置页面状态)。

用户故事:自定义视图与分类管理(优先级:P1) ​

作为用户,我希望在「自定义视图」里自己建分类、把创作归类或移出分类,以便把一堆页面按项目/用途整理起来。

为什么是这个优先级:默认视图只有「我发布的 / 共享给我的」两档,创作一多就无法按主题找回;分类是需求文档里唯一的内容组织手段。

独立测试:新建两个分类,把两个文件批量归类到第一个分类,验证列表只剩该分类的行、「全部」仍能看到全部;重命名分类验证列表 Tab 与行归属同步;删除一个有文件的分类,验证必须先选迁移目标才能确认;用重名与超 20 字名称验证校验;重启应用验证分类与归类关系仍在,换一个账号登录验证看不到上一个账号的分类(分类本地存储、按账号隔离)。

验收场景:

  1. 假设用户在分类行右侧的分段控件点击「自定义视图」,当视图切换完成,则分类行变为「全部」+ 自定义分类 + 「新建分类」,列表列出全部创作(含共享给我的行)并新增勾选列;再点「默认视图」回到「我发布的 / 共享给我的」,已建分类与其归类关系保留(不因切视图丢失)。
  2. 假设用户在「新建分类」输入一个 ≤20 字、与现有分类不重名的名称并确认,当创建成功,则新分类出现在分类行末尾并自动选中,列表切到该分类(此时为空态);名称为空、超过 20 字、与现有分类重名、或为保留名(「我发布的」「共享给我的」「全部」)时,确认按钮不可用或就地给出错误提示,不创建分类。
  3. 假设用户悬停某个自定义分类 Tab,当操作图标出现,则可「重命名」或「删除」;重命名沿用同一套名称校验,改名后 Tab 文案与其下文件的归属同步更新,文件名不变。
  4. 假设该分类下有 N 个文件,当用户确认删除分类,则必须先选择这些文件的迁移目标(另一分类,或「移出分类(未分类)」);未选目标时确认被拒并给出提示。分类内无文件时只需一次确认即可删除。
  5. 假设自定义视图下用户勾选了 2 行,当勾选生效,则列表上方出现批量条「已选 2 项 / 归类到… / 取消选择」;点「归类到…」弹出单选目标(含「移出分类(未分类)」),确认后这 2 行的分类被更新、勾选清空、当前分类列表就地刷新。
  6. 假设用户点击表头的全选框,当当前可见行未全选,则选中当前可见的全部行;已全选时再点则清空。
  7. 假设用户切换分组 Tab、切换视图或修改搜索词,当可见行集合发生变化,则已勾选项一律清空(不允许保留看不见的勾选项)。
  8. 假设用户把某文件归入 A 分类,当查看「全部」列表,则该文件仍出现在「全部」中(「全部」不因归类而丢失行);一个文件同一时刻只属于一个自定义分类。
  9. 假设用户建好分类并归类后关闭应用、再次打开创作集,当页面加载,则分类与其下的文件归属原样恢复(分类是本机持久化数据,不随应用重启丢失);在另一台机器登录同一账号时看不到这些分类(本期不做跨机器同步)。
  10. 假设本机切换了登录账号(登出后换另一个账号登录),当打开创作集的自定义视图,则只显示当前账号自己的分类与归属,上一个账号的分类不出现、也不被覆盖或删除。
  11. 假设分类的本地存储读取失败(文件损坏或不可写),当打开自定义视图,则页面提示「分类数据暂不可用」并允许继续用默认视图浏览;本次会话内的新建/归类照常生效(内存态),不因存储故障阻断操作。

用户故事:按文件名搜索(优先级:P2) ​

作为用户,我希望在创作集列表里按文件名模糊搜索,范围就是当前所在的分类,以便在几十个创作里快速定位。

为什么是这个优先级:分类解决「按主题分组」,搜索解决「记得名字找不到」;两者互补但不影响基本的浏览能力。

独立测试:在「全部」与某个自定义分类下分别输入同一关键词,验证结果只落在当前分类范围内;输入大小写混合的关键词验证大小写不敏感;清空搜索验证恢复完整列表且勾选已清空。

验收场景:

  1. 假设列表已加载,当用户在标题行右侧的搜索框输入关键词,则列表即时按文件名做大小写不敏感的子串匹配过滤,匹配范围仅限当前定位的分类/Tab(切换 Tab 后同一关键词在新范围内重新过滤)。
  2. 假设搜索框有内容,当用户点击清空按钮,则输入框清空、列表恢复当前分类的全部行、焦点回到搜索框。
  3. 假设搜索词无匹配,当过滤结果为空,则显示「没有匹配的文件」类空态(与「该分类为空」区分文案),不显示错误。
  4. 假设搜索框已有内容,当用户切换分组 Tab 或视图,则搜索词保留、在新范围内重新过滤(不因切 Tab 静默清空关键词)。
  5. 假设搜索触发了可见行变化,当过滤生效,则批量勾选被清空(同分类场景 7),不会出现「勾选了但看不见」的行。

用户故事:行内复制链接与在浏览器中打开(优先级:P1) ​

作为用户,我希望每一行都能直接被复制分享链接或用系统浏览器打开,以便把创作发给同事或在大窗口里查看。

为什么是这个优先级:这是列表最基本的两个出口,缺了它们列表只能看不能用。

独立测试:点「复制链接」验证剪贴板得到 {host}/code/artifact/{slug} 形态的链接并有轻提示;点「在浏览器中打开」验证用系统默认浏览器打开同一链接(不在应用内新开面板)。

验收场景:

  1. 假设用户在任一行点击「复制链接」,当复制成功,则剪贴板内容为该创作的 artifact URL({server}/code/artifact/{slug}),并出现「链接已复制」轻提示;剪贴板 API 不可用时回退到隐藏 textarea 复制路径,仍要有成功提示。
  2. 假设用户点击「在浏览器中打开」,则用系统默认浏览器打开该 URL(沿用桌面端 openExternal 语义),不在应用内新增 preview tab,也不替换当前 pane。
  3. 假设分享范围是「仅自己」,当用户复制链接发给同事,则对方打开会得到无权限页——页面不需要为此额外提示,链接语义保持与服务端一致(列表不臆断链接可用性)。
  4. 假设该行来自「共享给我的」,当用户复制链接或浏览器打开,则行为与自有行完全一致(共享行同样拿得到 URL)。

用户故事:分享设置与查看者版本(优先级:P1) ​

作为发布者,我希望在列表里直接改分享范围(仅自己 / 指定用户 / 企业成员)并选择查看者看到的版本(默认最新),以便不用打开网页外壳就能控制谁能看、看到哪一版。

为什么是这个优先级:分享是 artifact 的核心价值,页面外壳的 Share 菜单是唯一现状入口;列表里既然要能分享,就必须有这个编辑入口。

独立测试:对自有行打开分享弹层,切到「指定用户」搜索两位成员并勾选保存,验证服务端收到 mode=users 与这两个人的 userIds、列表该行「分享给」就地变成这两位、关闭后重开弹层回显这两位(已勾选);再切回「仅自己」保存后验证该行「分享给」变为「仅自己」且重开弹层回显「仅自己」;选择固定版本保存后重新打开弹层验证版本回显。

验收场景:

  1. 假设用户点击自有行的「分享」,当弹层打开,则显示文件名、三档分享范围(仅自己 / 指定用户 / 企业成员,单选)、查看者版本下拉(默认「最新版本」),并从服务端回显当前状态(分享范围、已勾选的指定用户、已固定的版本);列表中该行「分享给」列的展示与弹层回显始终一致。
  2. 假设用户选择「指定用户」,当范围生效,则出现成员搜索框与候选列表(按名字/邮箱模糊检索本企业 active 成员,输入即过滤);勾选/取消勾选即时反映在已选集合里,未勾选任何成员时点击「保存」被拒并就地提示「请至少选择 1 位用户」,不发请求。
  3. 假设成员检索接口失败(网络错误或 5xx),当用户输入关键词,则候选区显示错误与重试,已勾选内容保留;「仅自己」「企业成员」两档不受影响、仍可保存。
  4. 假设保存成功(任一档范围),当弹层关闭,则轻提示「分享设置已保存」,且该行「分享给」列就地更新为新的范围(仅自己 / 企业成员 / 团队成员+账号),不需要重新拉整个列表;请求失败时弹层保持打开并显示错误,列表数据不变。
  5. 假设用户把查看者版本从「最新版本」改成某个固定版本,当保存成功,则该创作对读者固定展示该版本(服务端 pinnedVersion),发布者自己仍看到最新版本;重新打开弹层时版本回显为已固定的那一版。
  6. 假设已分享的某成员已离职/被禁用,当重新打开弹层,则该账号仍按服务端返回的邮箱回显在已选列表里(标为不可选、仅展示),保存时以服务端校验结果为准(400 就地提示),不静默把人从列表里丢掉。
  7. 假设该行来自「共享给我的」,当查看操作列,则没有「分享」入口(分享范围只有拥有者能改),也不通过其它路径暴露编辑能力。
  8. 假设分享保存成功,当返回列表,则列表的排序、搜索词、勾选状态保持不变(分享是单行数据的写动作,不重排、不清空勾选)。

用户故事:撤销发布(优先级:P1) ​

作为发布者,我希望撤销某个创作的发布,使其立刻从所有创作集列表里消失、分享链接失效,但本地原文件不受影响,以便发错了或不想再对外展示时收回。

为什么是这个优先级:撤销是分享的相反操作,没有它发出去就收不回;需求文档明确「不删除原文件」,与「删除文件」是两件事。

独立测试:对有文件的分类…(不适用)——对自有行执行撤销发布,验证二次确认文案含文件名与「本地原文件不会被删除」,确认后该行从列表消失、原链接 404、本地文件仍在;共享行验证没有该入口。

验收场景:

  1. 假设用户在自有行的「分享」弹层里点击「撤销发布」(弹层页脚最左、默认次级色),当确认弹层出现,则文案说明「撤销这条发布记录与分享链接后,该文件不再在创作集列表中展示;本地原文件不会被删除。此操作不可撤销」,主按钮为危险色「撤销发布」,取消则回到分享弹层。
  2. 假设用户确认撤销,当请求成功(服务端删除该 slug 的发布记录),则该行从当前列表消失、轻提示「已撤销「{文件名}」的发布」,此后该 artifact URL 打开即无权限/不存在。
  3. 假设该创作正被应用内某个会话引用(如某个 pane 打开过它的预览),当撤销成功,则不主动关闭既有的预览/文件面板,只是后续访问该 URL 失败(按各面板既有失败语义展示,不弹出与新状态矛盾的成功提示)。
  4. 假设撤销请求失败(网络错误或服务端 5xx),当失败返回,则弹层保持打开并显示错误,列表不提前删除该行。
  5. 假设撤销的是共享给我的行,当查看可用操作,则没有撤销入口(只有拥有者能撤销自己的发布)。

用户故事:模板新建直达对话页(优先级:P1) ​

作为用户,我希望在创作集点「新建」后选一个模板(高保真原型 / 网页小游戏 / 效率工具 / 运营活动页 / 文档和模板 / 演示与教学 / 从零开始),直接回到对话页且输入框已预填该模板的提示词,以便不用自己想提示词就能开工;预填的提示词还要把「先本地出稿 → 每轮生成结束都让我在『继续调整』与『发布为创作(artifact)』之间选」这个节奏写进去,避免 AI 一生成完就直接发布,也避免它自己判断「已经定稿」。

为什么是这个优先级:创作集如果只有「看已有的」没有「做新的」,用户要回到空对话自己描述;模板把「做一个页面」的门槛降到一次点击。

独立测试:从创作集点「新建」,验证 7 张类型卡片与各自描述;点「高保真原型」验证回到当前会话、输入框已预填该模板提示词(正文 + 统一的发布节奏收尾)且光标在末尾(不新开会话、不切换会话);发送后验证用户消息、开场句与 3 条追问、生成中的右侧预览骨架;返回箭头验证回到类型选择页、再返回到创作集列表。

验收场景:

  1. 假设用户在创作集点击右上角「新建」,当面板打开,则显示 7 张类型卡片(高保真原型、网页小游戏、效率工具、运营活动页、文档和模板、演示与教学、从零开始),每张含图标、名称与一句描述;顶部有返回箭头回到创作集列表。
  2. 假设用户在一个已打开会话的情况下点击某张类型卡片,当回到对话页,则提示词预填进当前选中会话的输入框(不新开会话、不切换会话),文本可编辑、光标落在末尾,不直接发送;预填在点击后立即完成,不等任何新会话初始化。
  3. 假设用户点击任意一张类型卡片,当提示词预填进输入框,则其末尾固定带同一句收尾口径:先在本地做好 HTML/Markdown,每轮生成结束后都以「继续调整」与「发布为创作(artifact)」两个选项询问用户(用产品中文名,不写裸英文 Artifact)——七张卡一致,不因模板类型不同而有例外;用户选「继续调整」则继续按意见修改、不发布,选「发布为创作(artifact)」才发布;AI 不得自己判断已定稿而跳过这次询问,也不得在同一轮里生成完就直接发布。
  4. 假设用户在预填的提示词里填写占位内容后发送,当消息发出,则进入常规对话流:用户消息、AI 开场句与 3 条追问、右侧预览面板随生成出现(生成中为空态骨架);此时创作集页面已让位给该会话的对话页。
  5. 假设用户在类型选择页点击返回箭头,当返回生效,则回到创作集列表(保持进入时的视图/分组/搜索词);从对话页返回则回到类型选择页(两级返回,不跳过中间层)。
  6. 假设当前对话是一条刚打开的「新对话」(未发过消息),当点类型卡片,则提示词预填进这条空对话的输入框,不会因此再补开一条会话。
  7. 假设用户当前未选择过任何工作目录(新装、无最近目录),当点类型卡片,则提示词同样预填进当前输入框;点发送时按既有「请先选择工作目录」导流去选目录,已预填的内容不丢。

用户故事:对话中的 artifact 卡片(优先级:P2) ​

作为用户,当 AI 把页面发布到创作集后,我希望对话里出现一张卡片、点「在浏览器中打开」就能看这个 artifact,以便不用从回复正文里翻链接。

为什么是这个优先级:发布命令的原始结果是一段带 URL 的文本,用户要自己找、自己复制;卡片把「产物」和「查看」接起来,省掉从消息正文里翻链接这一步。

独立测试:让 AI 发布一次成功,验证对话里出现卡片(标题、版本与一个「在浏览器中打开」按钮)且工具行仍在、不显示原始结果文本;点击验证打开该 artifact 的页面 URL;失败与非发布动作验证不出卡片。

验收场景:

  1. 假设Artifact 工具以 publish 动作发布成功,当该轮结束,则该工具行(Artifact + 参数摘要)照常显示,卡片挂在其下方(与 Bash 工具「头部一行 + 内容在下面」同形态),卡片上为标题、已发布 · v{版本} 副信息与一个「在浏览器中打开」按钮;只顶掉该工具块的原始结果文本。
  2. 假设用户点击卡片上的「在浏览器中打开」,当动作生效,则用系统默认浏览器打开该 artifact 的页面 URL。
  3. 假设工具调用失败,或动作不是发布(list / read / 资源 / 评论),当该轮结束,则保持既有文本结果渲染,不出卡片。
  4. 假设卡片缺标题或版本(宿主拿到的附加信息不完整),当渲染卡片,则标题回落为 URL、副信息只写「已发布」,不出现空白卡片。

用户故事:自然语言识别「新建/更新创作集」(优先级:P3) ​

作为用户,我希望在对话里说「更新创作集」「新建创作集」时 AI 能理解为发布/更新 artifact,而不必说「发布成网页」,以便用产品里的名字指代这个动作。

为什么是这个优先级:这是提示词层面的对齐,可用性增强;现有的「发布/分享/做成网页/给链接」语义已能覆盖大多数表达。

独立测试:断言 Artifact 工具的 description 覆盖「创作集」字样,并有针对「新建创作集 → 发布新 artifact」「更新创作集 → 带 url 重新发布」的用例断言工具被调用。

验收场景:

  1. 假设用户说「把这份原型新建到创作集」,当模型据此调用 Artifact 工具,则按发布新 artifact 处理(无 url),结果出现在创作集「我发布的」列表。
  2. 假设用户说「更新创作集里的 codewave-pricing 页面」,当模型据此定位到该 artifact 并调用 Artifact 工具(带 url),则按重新发布处理、产生新版本,创作集里仍是同一行、最近发布时间刷新。
  3. 假设用户只是提到「创作集」但语境是浏览(如「我的创作集里有什么」),当模型处理,则走 list 动作而不是发布(工具 description 需区分「看列表」与「发布/更新」两类意图)。
  4. 假设enableArtifact 关闭或未登录,当用户这样描述,则与现状一致:工具不可用,模型如实说明能力不可用,不假装已经发布。

非功能需求 ​

  • 页面归属与形态:创作集是 desktop 独有整页,账号级(与 session/workdir/pane 无关);打开/关闭不销毁任何 pane,不改变当前会话选中项;从会话视图返回时恢复进入前的视图状态。
  • 数据来源单一:列表、分享、版本、撤销全部走 codechat artifact 接口,webview 不直连网络——由桌面端主进程带登录 token 请求,再经 host→webview 消息下发(沿用既有宿主命令/响应通道与 ReplyAttribution 归属登记)。
  • 写动作的口径:改分享、撤销发布是账号数据写动作,由桌面端主进程带 token 执行,失败时给可操作错误,不静默吞掉;分类与归类只写本机存储(桌面端主进程的 userData,按账号隔离),不产生网络请求,写失败只影响本机数据且要提示。
  • 刷新时机:只在「打开页面 / 手动刷新 / 本页完成的写动作之后 / 登录态翻转(登录成功、换账号、登出)」拉取列表;不做轮询、不做后台定时刷新;切 Tab/视图/搜索都是本地过滤,不重复请求。列表与分类都是账号级数据,登录成功或换账号后必须重取,不能停在打开那一刻的结果上。
  • 性能:列表一次拉取(服务端固定页大小),首屏 200 行内无虚拟滚动需求;搜索过滤在客户端完成(文件名子串匹配),输入即时响应(不做 debounce 卡顿);模板提示词点击即预填,不依赖(也不触发)新会话初始化。
  • 状态一致性:勾选、搜索词、视图/分组是页面内状态,关闭页面即丢弃(下次打开回到默认视图);分类与文件归属是本机持久化数据(按账号隔离、跨重启保留),跨机器/跨设备不同步——同一账号在另一台机器上看到的是空分类。
  • 鉴权:所有请求带当前登录 token;401 一律呈现为「需要登录」、403 呈现为「无权限」,两者都不呈现为空列表(403 不等于登录失效,不弹登录入口)。
  • 平台:仅 macOS/Windows/Linux 桌面端;VS Code 与 JetBrains 插件不提供创作集页面(沿用「发布走 /artifact 或自然语言」现状)。
  • 可测性:列表、搜索、分类、分享、撤销、模板新建都要有对应单测/组件测试(锚定 testid 与语义,不锚定图标字体类名);列表与分享的服务端调用在测试里 mock,不真实联网。

外部依赖与服务端补齐 ​

本期要 codechat 服务端补齐两处:①「普通成员可用的成员检索」接口(见第 2 条);② 列表接口 /api/frame/frames 每行补发布人与分享范围字段(见第 1 条)。列表、分享写入、版本、撤销用的都是 codechat 现有接口,分类存本机(第 5 条)、不涉及服务端。

两处服务端补齐已随 codechat MR !218 落地(GET /api/frame/frames 三字段 + GET /api/enterprise/members/search),最终形状见第 1、2 条末尾的「落地形状」。

  1. 列表字段(服务端需新增):GET /api/frame/frames 现在只返回 { slug, title, favicon, rel, updatedAt },没有发布人与分享范围/分享账号。列表要显示「发布人」「分享给」两列,服务端需按行补:发布者账号(ownerEmail,有显示名则一并给)、分享范围(shareMode:owner / users / org)、指定成员账号列表(sharedEmails,与 /view 的 sharedUsers 同源,一条 join 即可);rel = shared 的行同样返回 ownerEmail,shareMode/sharedEmails 可为空(前端该行「分享给」显示 —)。字段缺失时前端按「未知」降级(不报错、不误显「仅自己」)。

    落地形状(codechat MR !218):每行在原有五个字段之后追加这三项;ownerEmail 查不到时给空串 "",sharedEmails 在非 users 模式恒为 [](前端把空串/空数组都当作「缺省」);分页、排序与 rel 计算未变,取数固定两次查询。

  2. 成员检索接口(服务端需新增):GET /enterprise/members 现在要企业管理员(backend-enterprise/src/routes/enterprise.ts:33 的 requireEnterpriseAdmin),普通成员无法用于选人;现有 artifact 网页外壳只能靠「用户邮箱(逗号分隔)」文本框兜底(routes/artifacts.ts:1848、:1999)。本期需 codechat 提供一个普通成员可用的成员检索:按 search(名字/邮箱模糊)与分页返回本企业 active 成员的 { userId, email }(有显示名则一并返回),供弹层候选用。对齐 claude.ai 的做法:它用 GET /api/organizations/{org}/members_v2?search=<kw>&types[]=member 同时服务管理页与分享选人,并用 emails[] 批量反查已选邮箱对应的账号。 落地形状(codechat MR !218):GET /api/enterprise/members/search?search=&limit=,不挂 requireEnterpriseAdmin,返回 { members: [{ userId, email }] }(userId 可直接回填分享接口);limit 缺省 20、上限 50,非法值按缺省处理,超长 search 返回 400。

  3. 分享写入:POST /api/frame/{slug}/share 已支持 { mode, userIds | emails, pinnedVersion }(routes/artifacts.ts:718-790),指定用户以检索结果里的 userIds 提交即可,无需改协议。

  4. 分享弹层回显:GET /api/frame/{slug}/view(拥有者专用)已返回 perm.mode、pinnedVersion、sharedUsers({userId, email})、versions,正是弹层需要的三项回显数据(范围 / 已选成员 / 版本下拉),无需服务端改动。

  5. 分类存储(已定:本地):服务端不实现分类。分类与「文件 → 分类」归属由桌面端主进程写本机 userData(一个文件最多一个分类,按 slug 记),按当前登录账号隔离命名空间;服务端只需保证 slug 稳定(重发布不换 slug,现状即如此)。代价:换机器看不到分类,本期接受。

  6. 撤销发布:DELETE /api/frame/{slug} 已存在且语义吻合(删除该 slug 的发布记录与分享,不影响本地文件);前端只需二次确认与列表就地更新。

  7. 本地 slug 与分类的清理:某 slug 被撤销或不再可见时,本机那条归类记录保留(不主动删)——记录本身不影响列表渲染;分类里引用了已消失的 slug 时按「不可见即不显示」处理。

边界情况 ​

  • 登录态在页面打开期间失效:下一次请求返回 401 时整页切为「请先登录」,已渲染数据保留但不可再操作;401 之外的失败(含 403、5xx)只换错误提示,不切整页。
  • 多个窗口/多台机器同时改同一条分享:以最后一次保存为准(服务端覆盖写);本地不缓存分享状态,下次打开分享弹层按服务端当前状态回显。
  • 分类被删除时正有会话在显示它的文件:只影响创作集里的归属,不影响文件与预览。
  • 分类是本机数据:同一账号在本机开两个窗口时,写的是同一份存储、后写覆盖先写,另一窗口在下次打开页面时读到最新;不做跨窗口实时同步,也不与服务端对账。
  • 未登录:创作集本身打不开(列表需要登录),因此分类既不加载也不写入;登录后按新账号的命名空间重新加载。
  • 同一个 artifact 在「我发布的」与某分类下:默认视图与自定义视图是两个视图维度,行归属由 rel 与 cat 分别决定,不互相屏蔽。
  • 文件名过长:列表按列宽省略并以 title 提供全文;文件名列吸收剩余宽度,不截断到不可辨认。
  • 列表接口在共享行也返回分享范围:sharedEmails 目前对所有行都给(含 rel = shared),这是服务端待收窄的一处跨企业信息面;前端在共享行的「分享给」固定显示 —,页面不会把他人名单呈现出来,收窄由服务端决定(例如非拥有者只给人数)。
  • 「分享给」列内容过长:指定成员多于 2 位时只显示前两位加省略号,完整名单放 title 与分享弹层里;不因长文本撑宽列或挤压文件名列。
  • 共享行被对方撤销发布:下次拉取时该行消失;若用户正在操作该行(弹层已打开),保存请求收到 404 时提示「该创作已被撤销」并关闭弹层。
  • 分类名称含空白与 Emoji:按字面校验长度(≤20 字符)与重名(精确匹配),不做归一化之外的额外处理;纯空白名称视为空。
  • 成员检索的边界:关键词为空时按首批成员列出(或提示「输入关键字搜索」);无匹配显示空态文案;同名成员以邮箱区分;已分享但不在当前搜索结果里的成员仍留在「已选」区,不因过滤消失。
  • 侧栏处于收起状态进入创作集:页面照常打开(创作集页面自带返回箭头,不依赖侧栏),展开侧栏后入口选中态随之出现。

非目标(明确排除) ​

  • 评论:列表操作列的「评论」按钮、评论列表与发表、逐级回复、@ 引用成员、AI 是否解决标记、一键拉取评论命令——整块本期不做。
  • 订阅:对话中描述订阅、由 AI 完成订阅、订阅通知——本期不做。
  • 预览面板的「发布为 Artifact」按钮:发布只经 Artifact 工具(/artifact 命令或自然语言)发生,桌面端不在预览工具条上提供第二条发布入口(2026-10-22 与产品确认取消,原「预览窗口发布为 artifact」故事已删除)。
  • VS Code / JetBrains 的创作集页面:仅在桌面端提供。
  • 创作集的其它入口:不做侧栏以外的入口(如命令面板、深链、设置页入口);深链打开某个创作沿用既有 artifact URL 行为。
  • 列表分页/无限滚动:首版按服务端固定页大小一次拉取;超过一页再按需扩展。
  • 版本管理 UI:不在创作集里提供版本列表/回滚,只提供「查看者版本」这一项选择。
  • 「发布人」列的排序与搜索:只展示,不参与排序、不被文件名搜索命中(搜索口径仅文件名)。
  • 批量分享、批量撤销:批量只支持归类;分享与撤销逐条操作。