narraeon 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,7 @@ export function parseV1Envelope(value) {
21
21
  const requiredFields = {
22
22
  "preferences.save": { locale: "string" },
23
23
  "model.save": { connection: "object" },
24
+ "model.copy": { connectionId: "string", name: "string" },
24
25
  "model.select": { connectionId: "string" },
25
26
  "model.delete": { connectionId: "string" },
26
27
  "model.models": { provider: "string", baseUrl: "string" },
@@ -248,6 +249,7 @@ const requestTypes = new Set([
248
249
  "preferences.save",
249
250
  "model.read",
250
251
  "model.save",
252
+ "model.copy",
251
253
  "model.select",
252
254
  "model.delete",
253
255
  "model.models",
@@ -84,6 +84,8 @@ export class V1Runtime {
84
84
  return this.#models.view();
85
85
  case "model.save":
86
86
  return this.#models.save(request.connection);
87
+ case "model.copy":
88
+ return this.#models.copy(request.connectionId, request.name);
87
89
  case "model.select":
88
90
  return this.#models.select(request.connectionId);
89
91
  case "model.delete":
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { hasCLIProxyThinkingSuffix, modelReasoningPolicyIssue, modelProviderPresets, } from "../../protocol/modelConnections.js";
5
5
  const libraryFileName = "model-connections-v1.json";
6
6
  const maxModelListBytes = 1024 * 1024;
7
+ const maxStoredConnections = 64;
7
8
  export class ModelConnectionUnavailableError extends Error {
8
9
  constructor() {
9
10
  super("No V1 model connection is enabled");
@@ -33,6 +34,8 @@ export class ModelConnectionStore {
33
34
  : document.connections.findIndex(({ id }) => id === input.connectionId);
34
35
  if (input.connectionId !== undefined && index < 0)
35
36
  throw new Error("The model configuration to edit was not found");
37
+ if (index < 0 && document.connections.length >= maxStoredConnections)
38
+ throw new Error("The model configuration limit has been reached");
36
39
  const existing = index < 0 ? undefined : document.connections[index];
37
40
  const saved = {
38
41
  id: existing?.id ?? randomUUID(),
@@ -47,6 +50,27 @@ export class ModelConnectionStore {
47
50
  return this.#viewDocument(document);
48
51
  });
49
52
  }
53
+ copy(connectionId, name) {
54
+ return this.#mutate(async () => {
55
+ const document = await this.#readDocument();
56
+ const source = document.connections.find(({ id }) => id === connectionId);
57
+ if (source === undefined)
58
+ throw new Error("The model configuration to copy was not found");
59
+ if (document.connections.length >= maxStoredConnections)
60
+ throw new Error("The model configuration limit has been reached");
61
+ const copied = {
62
+ ...structuredClone(source),
63
+ id: randomUUID(),
64
+ name: requiredTrimmed(name, "Configuration name", 160),
65
+ };
66
+ document.connections.push(copied);
67
+ await this.#writeDocument(document);
68
+ return {
69
+ library: this.#viewDocument(document),
70
+ copiedConnectionId: copied.id,
71
+ };
72
+ });
73
+ }
50
74
  select(connectionId) {
51
75
  return this.#mutate(async () => {
52
76
  const document = await this.#readDocument();
@@ -90,8 +114,8 @@ export class ModelConnectionStore {
90
114
  const stored = (await this.#readDocument()).connections.find(({ id }) => id === input.connectionId);
91
115
  if (stored === undefined)
92
116
  throw new Error("Model configuration not found");
93
- if (stored.provider !== provider || stored.baseUrl !== baseUrl)
94
- throw new Error("The endpoint or protocol changed; enter the API key again before fetching models");
117
+ // A saved connection owns its credential across later protocol and
118
+ // endpoint edits; an explicit key in this request still takes precedence.
95
119
  apiKey = stored.apiKey;
96
120
  }
97
121
  if (apiKey === undefined)
@@ -225,14 +249,11 @@ function normalizeSaveInput(input, existing) {
225
249
  maxOutputTokens: input.maxOutputTokens,
226
250
  });
227
251
  const explicitApiKey = normalizeOptionalApiKey(input.apiKey);
228
- const apiKey = explicitApiKey ??
229
- (existing?.provider === provider && existing.baseUrl === baseUrl
230
- ? existing.apiKey
231
- : undefined);
252
+ // Credential reuse follows the saved configuration identity, not its current
253
+ // protocol or endpoint fields.
254
+ const apiKey = explicitApiKey ?? existing?.apiKey;
232
255
  if (apiKey === undefined)
233
- throw new Error(existing === undefined
234
- ? "A new model configuration requires an API key"
235
- : "The endpoint or protocol changed; enter the API key again because old credentials are never forwarded to a new endpoint");
256
+ throw new Error("A new model configuration requires an API key");
236
257
  validatePresetBinding(presetId, provider, baseUrl);
237
258
  return {
238
259
  name,
@@ -256,7 +277,7 @@ function validateDocument(value) {
256
277
  (value.activeConnectionId !== null &&
257
278
  typeof value.activeConnectionId !== "string") ||
258
279
  !Array.isArray(value.connections) ||
259
- value.connections.length > 64)
280
+ value.connections.length > maxStoredConnections)
260
281
  throw new Error("Model-connection configuration file does not match its fixed schema");
261
282
  const connections = value.connections.map(validateStoredConnection);
262
283
  if (new Set(connections.map(({ id }) => id)).size !== connections.length)
@@ -1027,7 +1027,7 @@ $document:
1027
1027
  ---
1028
1028
 
1029
1029
  # Example rule
1030
- `,"输入 UTF-8 文本内容……":`Enter UTF-8 text…`,确认修正内容:`Review correction`,份文档会变化:`document(s) will change`,修正前:`Before correction`,修正后:`After correction`,"查看内容 hash":`View content hash`,"查看端点、附加材料和下一次提示词":`View the endpoint, additional material, and next prompt`,父端点:`Parent endpoint`,候选版本:`Candidate version`,完整附加材料清单差异:`Complete additional-material diff`,下一次真实提示词:`Next real Prompt Preview`,应用这笔修正:`Apply this correction`,放弃修正草稿:`Discard correction draft`,世界文件:`World files`,"当前 state 文档":`Current state documents`,尚无已提交叙事:`No committed narrative yet`,"Runtime 诊断":`Runtime diagnostics`,"创建 operation":`Creation operation`,历史条目:`History entries`,没有文件:`No files`,继续游玩:`Continue playing`,世界:`Worlds`,进入世界:`Open world`,删除:`Delete`,世界名称:`World name`,保存世界名称:`Save world name`,保存:`Save`,取消:`Cancel`,还没有正在游玩的世界:`No worlds in play yet`,"先新建内容包,或上传一份 ZIP,再整理开场与世界文档并创建独立世界。":`Create a content package or upload a ZIP, then prepare its opening and world documents and create an independent world.`,工作区状态:`Workspace status`,当前预设:`Current preset`,尚未选择:`Not selected`,可用内容包:`Usable content packages`,份:`total`,"内容包是世界模板;创建后的世界会独立保存状态、控制与历史。":`A content package is a world template. A created world stores its own state, control, and history.`,创作工作台:`Authoring workbench`,"四个入口各自完成一件事,运行中的世界从上方直接进入。":`Each of the four entries has one job. Open active worlds directly above.`,四个任务入口:`Four task entries`,内容编辑:`Content editing`,"编辑内容包里的开场、世界文档与控制文件。":`Edit a content package's opening, world documents, and control files.`,从空白内容包开始:`Start with a blank content package`,预设:`Presets`,"编辑提示块库、叙事规则、后置请求与扩展资源。":`Edit prompt blocks, narrative rules, follow-up requests, and extension resources.`,选择预设:`Choose a preset`,"从一份可用内容包复制出独立演化的世界。":`Copy a usable content package into an independently evolving world.`,需要可用内容包:`Usable content package required`,提示词预览:`Prompt Preview`,"检查真实 role、材料、工具、预算与缓存边界。":`Inspect real roles, material, tools, budgets, and cache boundaries.`,模型已就绪:`Model ready`,需要模型连接:`Model connection required`,新建内容包:`Create content package`,待修复:`Needs repair`,"可编辑、预览并创建新世界":`Ready to edit, preview, and create a world`,打开并修复当前树中的问题:`Open and repair issues in the current tree`,从一组人类可读文件开始:`Start with human-readable files`,"内容包由 opening.md、world 与 control 组成,可以从空白新建,也可以上传一份内容包 ZIP。":`A content package contains opening.md, world, and control. Create one from scratch or upload a content-package ZIP.`,"导入 ZIP":`Import ZIP`,"选择本应用导出或遵循相同结构的内容包 ZIP;导入会创建新的本地身份,不覆盖同名内容包。":`Choose a ZIP exported by this app or one with the same structure. Import creates a new local identity and never overwrites a package with the same name.`,"内容包 ZIP":`Content-package ZIP`,"内容包 ZIP 文件":`Content-package ZIP file`,"选择 ZIP 文件":`Choose ZIP file`,"正在导入…":`Importing…`,默认模型:`Default model`,"模型连接已保存并启用;后续请求只使用当前配置。":`The model connection was saved and enabled. Later requests use only the current configuration.`,"已切换当前模型配置;Runtime 不会自动故障转移。":`The current model configuration was switched. Runtime does not fail over automatically.`,"模型配置已从本机删除。":`The model configuration was deleted from this device.`,"保存多份本机配置并明确切换。API Key 不会返回浏览器;切换失败时 Runtime 不会悄悄改用另一份配置。":`Save multiple local configurations and switch explicitly. API keys are never returned to the browser, and Runtime does not silently use another configuration when a switch fails.`,当前配置:`Current configuration`,尚未启用:`Not enabled`,先保存一份可用连接:`Save a usable connection first`,新建配置:`New configuration`,编辑配置:`Edit configuration`,新建另一份:`Create another`,提供商:`Provider`,配置名称:`Configuration name`,协议适配器:`Protocol adapter`,已填入:`Using`,"的内置端点;手动修改后按自定义端点保存。":`'s built-in endpoint. After a manual edit it will be saved as a custom endpoint.`,新配置必填:`Required for a new configuration`,"端点或协议已改变,必须重新填写":`The endpoint or protocol changed; enter it again`,留空以保留当前端点的现有凭据:`Leave blank to keep the existing credential for this endpoint`,"旧凭据只会为同一协议和端点保留,不会静默转发到新端点。":`An old credential is retained only for the same protocol and endpoint; it is never silently forwarded to a new endpoint.`,"模型 ID":`Model ID`,"正在拉取…":`Fetching…`,从端点拉取模型:`Fetch models from endpoint`,"可在模型 ID 输入框中选择已拉取结果。":`Fetched results can be selected in the Model ID field.`,"真实 context window":`Actual context window`,"最大输出 tokens":`Maximum output tokens`,"正在保存…":`Saving…`,保存模型连接并启用:`Save and enable model connection`,放弃修改:`Discard changes`,重置新配置:`Reset new configuration`,已保存模型配置:`Saved model configurations`,已保存配置:`Saved configurations`,"尚未保存模型配置。":`No model configurations saved yet.`,当前使用:`In use`,模型:`Model`,端点:`Endpoint`,"窗口 / 输出":`Window / output`,凭据:`Credential`,已保存在本机:`Saved on this device`,未配置:`Not configured`,"正在切换…":`Switching…`,切换到此配置:`Switch to this configuration`,正在编辑:`Editing`,编辑:`Edit`,"正在删除…":`Deleting…`,"先切换到另一份配置,才能删除当前配置。":`Switch to another configuration before deleting the current one.`,模型配置操作失败:`Model-configuration action failed`};Object.assign(A,{玩法预设操作失败:`Play-preset action failed`,"当前玩法有未保存修改;请先保存或撤销,再切换预设。":`The current play preset has unsaved changes. Save or discard them before switching presets.`,"已撤销当前未保存修改。":`Current unsaved changes were discarded.`,"preset.yaml/call-chain.yaml 与结构化编辑同时修改;请保留一种编辑方式后再保存,避免静默覆盖。":`preset.yaml/call-chain.yaml and the structured editor were both changed. Keep one version before saving to avoid a silent overwrite.`,"玩法文件与结构化草稿已保存。":`Play files and the structured draft were saved.`,"玩法预设业务文件已导出。":`Play-preset files were exported.`,"导入文件必须是 UTF-8 玩法业务文件数组":`The imported file must be an array of UTF-8 play files`,导入玩法:`Import play preset`,"玩法预设已导入为新的本地身份;请显式启用其中的 JavaScript。":`The play preset was imported as a new local identity. Enable its JavaScript explicitly if you trust it.`,玩法预设:`Play presets`,"在同一处管理设定完善方法、主持规则、调用链、界面产物与可信本地代码。":`Manage setting-improvement methods, host rules, call chains, interface artifacts, and trusted local code in one place.`,新调用链与设定完善当前使用:`New call chains and setting-improvement sessions currently use`,未选择:`Not selected`,"已经开始的模型会话继续使用冻结 revision":`Model sessions already in progress keep their frozen revision`,玩法预设工作区:`Play-preset workspace`,玩法预设列表:`Play-preset list`,本地预设:`Local presets`,"选择一个本地身份开始编辑。":`Choose a local identity to start editing.`,当前玩法:`Current play preset`,结构有效:`Structure valid`,需要修复:`Needs repair`,已停用:`Disabled`,新建与导入玩法预设:`Create and import play presets`,新建或导入:`Create or import`,新玩法预设名称:`New play-preset name`,"已新建普通玩法预设。":`A regular play preset was created.`,新建空白预设:`Create blank preset`,复制推荐:`Copy recommended preset`,导入玩法文件:`Import play files`,导入玩法预设文件:`Import play-preset file`,"还没有玩法预设。":`There are no play presets yet.`,玩法预设文件编辑器:`Play-preset file editor`,"· 修改只影响之后开始的全新上下文":`· Changes affect only new contexts started later`,未保存修改:`Unsaved changes`,已保存:`Saved`,"JavaScript 已启用":`JavaScript enabled`,"JavaScript 已停用":`JavaScript disabled`,预设名称:`Preset name`,玩法预设名称:`Play-preset name`,预设操作:`Preset actions`,玩法预设身份管理:`Play-preset identity management`,"这些操作只管理这份本地预设;内容编辑和保存仍在页面底部完成。":`These actions manage only this local preset. Edit and save its content at the bottom of the page.`,"玩法预设状态已更新。":`Play-preset status updated.`,启用预设:`Enable preset`,停用预设:`Disable preset`,"JavaScript 已停用;raw/document 仍可预览。":`JavaScript is disabled; raw and document content can still be previewed.`,"JavaScript 已显式启用(本地可信代码)。":`JavaScript is explicitly enabled as trusted local code.`,"停用 JavaScript":`Disable JavaScript`,"启用 JavaScript(本地可信代码)":`Enable JavaScript (trusted local code)`,"已复制为独立本地身份。":`Copied as an independent local identity.`,复制为新预设:`Copy as new preset`,导出业务文件:`Export play files`,"玩法预设已删除;删空后会自动重建默认预设。":`The play preset was deleted. Deleting the last preset automatically rebuilds the default.`,删除预设:`Delete preset`,"导入的 JavaScript 默认停用;启用表示你信任这些本地文件,而不是获得安全沙箱保证。":`Imported JavaScript is disabled by default. Enabling it means you trust these local files; it does not provide a security sandbox.`,玩法预设摘要:`Play-preset summary`,后置请求:`Follow-up requests`,产物输出:`Artifact outputs`,界面挂载:`Interface mounts`,普通文件:`Regular files`,玩法预设编辑区域:`Play-preset editing area`,"该玩法文件路径已经存在。":`That play-file path already exists.`,"已加入普通文件草稿;保存时会通过 codec 校验。":`The regular file was added to the draft and will be validated by the codec when saved.`,结构化编辑暂不可用:`Structured editing is unavailable`,"当前草稿无法生成结构投影。请到“高级文件”修复 preset.yaml 或 call-chain.yaml,保存后再回来。":`The current draft cannot produce a structured projection. Repair preset.yaml or call-chain.yaml under Advanced files, save, and return here.`,前往高级文件:`Go to Advanced files`,预览当前预设:`Preview current preset`,"产物外观与真实调用链提示词都留在这里检查,不会调用模型或离开当前预设。":`Inspect artifact appearance and real call-chain prompts here without calling a model or leaving the current preset.`,"正在生成真实编译/产物预览…":`Generating real compilation and artifact previews…`,工作台静态错误:`Workbench static error`,当前预设的真实提示词预览:`Real prompt preview for the current preset`,真实调用链预览:`Real call-chain preview`,"请先保存当前修改;真实预览只编译已冻结的有效 revision。":`Save the current changes first. A real preview compiles only a frozen valid revision.`,"当前 revision 需要修复,暂时不能编译真实调用链。":`The current revision needs repair and cannot compile a real call chain yet.`,"当前宿主没有提供提示词预览面板。":`The current host does not provide a Prompt Preview panel.`,"preset.yaml/call-chain.yaml 与结构化字段均有未保存修改;请撤销其中一侧后再保存,避免 stale structure 覆盖 raw YAML。":`preset.yaml/call-chain.yaml and structured fields both have unsaved changes. Discard one side before saving so stale structure cannot overwrite raw YAML.`,草稿尚未保存:`Draft not saved`,结构校验通过:`Structure validation passed`,草稿需要修复:`Draft needs repair`,撤销未保存修改:`Discard unsaved changes`,保存修改:`Save changes`,"已将该冻结 revision 设为当前玩法。":`That frozen revision is now the current play preset.`,应用为当前玩法:`Use as current play preset`,"AI 设定完善":`Setting improvement`,"这份文字决定 AI 怎样理解、规划和创作内容包设定;每次开始完善时会冻结当前预设 revision。":`This text tells the model how to understand, plan, and author content-package settings. Each session freezes the current preset revision.`,"工具为什么不在这里?":`Why are the tools not editable here?`,"Runtime 继续内置 setting_* 工具定义、参数、说明、只读/写入阶段边界和候选终态协议。预设只能编辑创作语义,不能替换这些机械契约。":`Runtime owns the setting_* definitions, parameters, descriptions, read/write phase boundaries, and candidate settlement protocol. A preset can edit authoring semantics but cannot replace these mechanical contracts.`,沿用系统推荐提示:`Use the system-recommended prompt`,"这是一份功能加入前保存的 v1 预设。打开页面不会改写它;写入后才会产生新的预设 revision。":`This v1 preset was saved before the feature existed. Opening this page does not rewrite it; writing the prompt creates a new preset revision.`,系统推荐设定完善提示词:`System-recommended setting-improvement prompt`,写入预设并编辑:`Write into preset and edit`,设定完善创作提示:`Setting-improvement authoring prompt`,"# 叙事规则\n\n说明 AI 每次写玩家可见正文时都应遵守的规则。\n":`# Narrative rules
1030
+ `,"输入 UTF-8 文本内容……":`Enter UTF-8 text…`,确认修正内容:`Review correction`,份文档会变化:`document(s) will change`,修正前:`Before correction`,修正后:`After correction`,"查看内容 hash":`View content hash`,"查看端点、附加材料和下一次提示词":`View the endpoint, additional material, and next prompt`,父端点:`Parent endpoint`,候选版本:`Candidate version`,完整附加材料清单差异:`Complete additional-material diff`,下一次真实提示词:`Next real Prompt Preview`,应用这笔修正:`Apply this correction`,放弃修正草稿:`Discard correction draft`,世界文件:`World files`,"当前 state 文档":`Current state documents`,尚无已提交叙事:`No committed narrative yet`,"Runtime 诊断":`Runtime diagnostics`,"创建 operation":`Creation operation`,历史条目:`History entries`,没有文件:`No files`,继续游玩:`Continue playing`,世界:`Worlds`,进入世界:`Open world`,删除:`Delete`,世界名称:`World name`,保存世界名称:`Save world name`,保存:`Save`,取消:`Cancel`,还没有正在游玩的世界:`No worlds in play yet`,"先新建内容包,或上传一份 ZIP,再整理开场与世界文档并创建独立世界。":`Create a content package or upload a ZIP, then prepare its opening and world documents and create an independent world.`,工作区状态:`Workspace status`,当前预设:`Current preset`,尚未选择:`Not selected`,可用内容包:`Usable content packages`,份:`total`,"内容包是世界模板;创建后的世界会独立保存状态、控制与历史。":`A content package is a world template. A created world stores its own state, control, and history.`,创作工作台:`Authoring workbench`,"四个入口各自完成一件事,运行中的世界从上方直接进入。":`Each of the four entries has one job. Open active worlds directly above.`,四个任务入口:`Four task entries`,内容编辑:`Content editing`,"编辑内容包里的开场、世界文档与控制文件。":`Edit a content package's opening, world documents, and control files.`,从空白内容包开始:`Start with a blank content package`,预设:`Presets`,"编辑提示块库、叙事规则、后置请求与扩展资源。":`Edit prompt blocks, narrative rules, follow-up requests, and extension resources.`,选择预设:`Choose a preset`,"从一份可用内容包复制出独立演化的世界。":`Copy a usable content package into an independently evolving world.`,需要可用内容包:`Usable content package required`,提示词预览:`Prompt Preview`,"检查真实 role、材料、工具、预算与缓存边界。":`Inspect real roles, material, tools, budgets, and cache boundaries.`,模型已就绪:`Model ready`,需要模型连接:`Model connection required`,新建内容包:`Create content package`,待修复:`Needs repair`,"可编辑、预览并创建新世界":`Ready to edit, preview, and create a world`,打开并修复当前树中的问题:`Open and repair issues in the current tree`,从一组人类可读文件开始:`Start with human-readable files`,"内容包由 opening.md、world 与 control 组成,可以从空白新建,也可以上传一份内容包 ZIP。":`A content package contains opening.md, world, and control. Create one from scratch or upload a content-package ZIP.`,"导入 ZIP":`Import ZIP`,"选择本应用导出或遵循相同结构的内容包 ZIP;导入会创建新的本地身份,不覆盖同名内容包。":`Choose a ZIP exported by this app or one with the same structure. Import creates a new local identity and never overwrites a package with the same name.`,"内容包 ZIP":`Content-package ZIP`,"内容包 ZIP 文件":`Content-package ZIP file`,"选择 ZIP 文件":`Choose ZIP file`,"正在导入…":`Importing…`,默认模型:`Default model`,"模型连接已保存并启用;后续请求只使用当前配置。":`The model connection was saved and enabled. Later requests use only the current configuration.`,"已切换当前模型配置;Runtime 不会自动故障转移。":`The current model configuration was switched. Runtime does not fail over automatically.`,"模型配置已从本机删除。":`The model configuration was deleted from this device.`,"已克隆为「{name}」。副本保留本机凭据,但不会切换当前配置。":`Cloned as “{name}”. The copy keeps the locally stored credential without switching the active configuration.`,"保存多份本机配置并明确切换。API Key 不会返回浏览器;切换失败时 Runtime 不会悄悄改用另一份配置。":`Save multiple local configurations and switch explicitly. API keys are never returned to the browser, and Runtime does not silently use another configuration when a switch fails.`,当前配置:`Current configuration`,尚未启用:`Not enabled`,先保存一份可用连接:`Save a usable connection first`,新建配置:`New configuration`,编辑配置:`Edit configuration`,新建另一份:`Create another`,提供商:`Provider`,配置名称:`Configuration name`,协议适配器:`Protocol adapter`,已填入:`Using`,"的内置端点;手动修改后按自定义端点保存。":`'s built-in endpoint. After a manual edit it will be saved as a custom endpoint.`,新配置必填:`Required for a new configuration`,留空以沿用本机保存的现有凭据:`Leave blank to reuse the existing credential saved on this device`,"编辑已有配置时,API Key 留空会把本机保存的现有凭据用于当前协议和端点;填写新值会覆盖它。":`When editing an existing configuration, leaving API Key blank reuses the credential saved on this device for the current protocol and endpoint. Enter a new value to replace it.`,"模型 ID":`Model ID`,"正在拉取…":`Fetching…`,从端点拉取模型:`Fetch models from endpoint`,"可在模型 ID 输入框中选择已拉取结果。":`Fetched results can be selected in the Model ID field.`,"真实 context window":`Actual context window`,"最大输出 tokens":`Maximum output tokens`,"正在保存…":`Saving…`,保存模型连接并启用:`Save and enable model connection`,放弃修改:`Discard changes`,重置新配置:`Reset new configuration`,已保存模型配置:`Saved model configurations`,已保存配置:`Saved configurations`,"尚未保存模型配置。":`No model configurations saved yet.`,当前使用:`In use`,模型:`Model`,端点:`Endpoint`,"窗口 / 输出":`Window / output`,凭据:`Credential`,已保存在本机:`Saved on this device`,未配置:`Not configured`,"正在切换…":`Switching…`,切换到此配置:`Switch to this configuration`,"(副本)":` (copy)`,"(副本 {number})":` (copy {number})`,"正在克隆…":`Cloning…`,克隆配置:`Clone configuration`,"Runtime 没有返回已克隆的模型配置":`Runtime did not return the cloned model configuration`,无法生成不重复的副本名称:`Could not generate a unique copy name`,正在编辑:`Editing`,编辑:`Edit`,"正在删除…":`Deleting…`,"先切换到另一份配置,才能删除当前配置。":`Switch to another configuration before deleting the current one.`,模型配置操作失败:`Model-configuration action failed`};Object.assign(A,{玩法预设操作失败:`Play-preset action failed`,"当前玩法有未保存修改;请先保存或撤销,再切换预设。":`The current play preset has unsaved changes. Save or discard them before switching presets.`,"已撤销当前未保存修改。":`Current unsaved changes were discarded.`,"preset.yaml/call-chain.yaml 与结构化编辑同时修改;请保留一种编辑方式后再保存,避免静默覆盖。":`preset.yaml/call-chain.yaml and the structured editor were both changed. Keep one version before saving to avoid a silent overwrite.`,"玩法文件与结构化草稿已保存。":`Play files and the structured draft were saved.`,"玩法预设业务文件已导出。":`Play-preset files were exported.`,"导入文件必须是 UTF-8 玩法业务文件数组":`The imported file must be an array of UTF-8 play files`,导入玩法:`Import play preset`,"玩法预设已导入为新的本地身份;请显式启用其中的 JavaScript。":`The play preset was imported as a new local identity. Enable its JavaScript explicitly if you trust it.`,玩法预设:`Play presets`,"在同一处管理设定完善方法、主持规则、调用链、界面产物与可信本地代码。":`Manage setting-improvement methods, host rules, call chains, interface artifacts, and trusted local code in one place.`,新调用链与设定完善当前使用:`New call chains and setting-improvement sessions currently use`,未选择:`Not selected`,"已经开始的模型会话继续使用冻结 revision":`Model sessions already in progress keep their frozen revision`,玩法预设工作区:`Play-preset workspace`,玩法预设列表:`Play-preset list`,本地预设:`Local presets`,"选择一个本地身份开始编辑。":`Choose a local identity to start editing.`,当前玩法:`Current play preset`,结构有效:`Structure valid`,需要修复:`Needs repair`,已停用:`Disabled`,新建与导入玩法预设:`Create and import play presets`,新建或导入:`Create or import`,新玩法预设名称:`New play-preset name`,"已新建普通玩法预设。":`A regular play preset was created.`,新建空白预设:`Create blank preset`,复制推荐:`Copy recommended preset`,导入玩法文件:`Import play files`,导入玩法预设文件:`Import play-preset file`,"还没有玩法预设。":`There are no play presets yet.`,玩法预设文件编辑器:`Play-preset file editor`,"· 修改只影响之后开始的全新上下文":`· Changes affect only new contexts started later`,未保存修改:`Unsaved changes`,已保存:`Saved`,"JavaScript 已启用":`JavaScript enabled`,"JavaScript 已停用":`JavaScript disabled`,预设名称:`Preset name`,玩法预设名称:`Play-preset name`,预设操作:`Preset actions`,玩法预设身份管理:`Play-preset identity management`,"这些操作只管理这份本地预设;内容编辑和保存仍在页面底部完成。":`These actions manage only this local preset. Edit and save its content at the bottom of the page.`,"玩法预设状态已更新。":`Play-preset status updated.`,启用预设:`Enable preset`,停用预设:`Disable preset`,"JavaScript 已停用;raw/document 仍可预览。":`JavaScript is disabled; raw and document content can still be previewed.`,"JavaScript 已显式启用(本地可信代码)。":`JavaScript is explicitly enabled as trusted local code.`,"停用 JavaScript":`Disable JavaScript`,"启用 JavaScript(本地可信代码)":`Enable JavaScript (trusted local code)`,"已复制为独立本地身份。":`Copied as an independent local identity.`,复制为新预设:`Copy as new preset`,导出业务文件:`Export play files`,"玩法预设已删除;删空后会自动重建默认预设。":`The play preset was deleted. Deleting the last preset automatically rebuilds the default.`,删除预设:`Delete preset`,"导入的 JavaScript 默认停用;启用表示你信任这些本地文件,而不是获得安全沙箱保证。":`Imported JavaScript is disabled by default. Enabling it means you trust these local files; it does not provide a security sandbox.`,玩法预设摘要:`Play-preset summary`,后置请求:`Follow-up requests`,产物输出:`Artifact outputs`,界面挂载:`Interface mounts`,普通文件:`Regular files`,玩法预设编辑区域:`Play-preset editing area`,"该玩法文件路径已经存在。":`That play-file path already exists.`,"已加入普通文件草稿;保存时会通过 codec 校验。":`The regular file was added to the draft and will be validated by the codec when saved.`,结构化编辑暂不可用:`Structured editing is unavailable`,"当前草稿无法生成结构投影。请到“高级文件”修复 preset.yaml 或 call-chain.yaml,保存后再回来。":`The current draft cannot produce a structured projection. Repair preset.yaml or call-chain.yaml under Advanced files, save, and return here.`,前往高级文件:`Go to Advanced files`,预览当前预设:`Preview current preset`,"产物外观与真实调用链提示词都留在这里检查,不会调用模型或离开当前预设。":`Inspect artifact appearance and real call-chain prompts here without calling a model or leaving the current preset.`,"正在生成真实编译/产物预览…":`Generating real compilation and artifact previews…`,工作台静态错误:`Workbench static error`,当前预设的真实提示词预览:`Real prompt preview for the current preset`,真实调用链预览:`Real call-chain preview`,"请先保存当前修改;真实预览只编译已冻结的有效 revision。":`Save the current changes first. A real preview compiles only a frozen valid revision.`,"当前 revision 需要修复,暂时不能编译真实调用链。":`The current revision needs repair and cannot compile a real call chain yet.`,"当前宿主没有提供提示词预览面板。":`The current host does not provide a Prompt Preview panel.`,"preset.yaml/call-chain.yaml 与结构化字段均有未保存修改;请撤销其中一侧后再保存,避免 stale structure 覆盖 raw YAML。":`preset.yaml/call-chain.yaml and structured fields both have unsaved changes. Discard one side before saving so stale structure cannot overwrite raw YAML.`,草稿尚未保存:`Draft not saved`,结构校验通过:`Structure validation passed`,草稿需要修复:`Draft needs repair`,撤销未保存修改:`Discard unsaved changes`,保存修改:`Save changes`,"已将该冻结 revision 设为当前玩法。":`That frozen revision is now the current play preset.`,应用为当前玩法:`Use as current play preset`,"AI 设定完善":`Setting improvement`,"这份文字决定 AI 怎样理解、规划和创作内容包设定;每次开始完善时会冻结当前预设 revision。":`This text tells the model how to understand, plan, and author content-package settings. Each session freezes the current preset revision.`,"工具为什么不在这里?":`Why are the tools not editable here?`,"Runtime 继续内置 setting_* 工具定义、参数、说明、只读/写入阶段边界和候选终态协议。预设只能编辑创作语义,不能替换这些机械契约。":`Runtime owns the setting_* definitions, parameters, descriptions, read/write phase boundaries, and candidate settlement protocol. A preset can edit authoring semantics but cannot replace these mechanical contracts.`,沿用系统推荐提示:`Use the system-recommended prompt`,"这是一份功能加入前保存的 v1 预设。打开页面不会改写它;写入后才会产生新的预设 revision。":`This v1 preset was saved before the feature existed. Opening this page does not rewrite it; writing the prompt creates a new preset revision.`,系统推荐设定完善提示词:`System-recommended setting-improvement prompt`,写入预设并编辑:`Write into preset and edit`,设定完善创作提示:`Setting-improvement authoring prompt`,"# 叙事规则\n\n说明 AI 每次写玩家可见正文时都应遵守的规则。\n":`# Narrative rules
1031
1031
 
1032
1032
  Describe the rules the model must follow whenever it writes player-visible prose.
1033
1033
  `,"# 新后置请求\n\n说明主调用链完成后,需要额外整理成什么界面内容。\n":`# New follow-up request
@@ -1177,7 +1177,7 @@ $document:
1177
1177
 
1178
1178
  # 示例规则
1179
1179
  `):j(`输入 UTF-8 文本内容……`)}function ji(e){return e.length===0?0:e.split(`
1180
- `).length}function Mi({contentPackages:e,worlds:t,selectedPackageId:n,modelConfigured:r,activeModelName:i,currentPresetName:a,importArchive:o,importPending:s,onImportArchiveChange:c,onEditContent:u,onOpenPlayPresets:d=()=>void 0,onCreateWorld:f,onOpenPreview:p,onCreatePackage:m,onImportPackage:h,onOpenPackage:g,onOpenWorld:_,onRenameWorld:v,onDeleteWorld:y}){let[b,x]=(0,l.useState)(null),[S,C]=(0,l.useState)(``),w=e.filter(({status:e})=>e===`usable`).length,T=e.length-w;return(0,K.jsxs)(`div`,{className:`home-dashboard`,children:[(0,K.jsxs)(`div`,{className:`home-primary-grid`,children:[(0,K.jsxs)(`section`,{className:`home-surface home-world-library`,"aria-labelledby":`home-worlds-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`PLAY`}),(0,K.jsx)(`h2`,{id:`home-worlds-title`,children:j(`继续游玩`)})]}),(0,K.jsx)(`span`,{className:`home-count`,"aria-label":j(`{count} 个世界`,{count:t.length}),children:t.length.toLocaleString(ie())})]}),t.length>0?(0,K.jsx)(`div`,{className:`home-world-grid`,children:t.map((e,t)=>(0,K.jsxs)(`div`,{className:`home-world-card-shell`,children:[(0,K.jsxs)(`button`,{className:`home-world-card`,type:`button`,"aria-label":j(`打开世界:{title}`,{title:e.title}),disabled:s,onClick:()=>_(e.worldId),children:[(0,K.jsxs)(`span`,{className:`home-world-card-meta`,children:[j(`世界`),String(t+1).padStart(2,`0`)]}),(0,K.jsx)(`strong`,{children:e.title}),(0,K.jsxs)(`span`,{className:`home-world-card-action`,children:[j(`进入世界`),(0,K.jsx)(`span`,{"aria-hidden":`true`,children:`→`})]})]}),(0,K.jsxs)(`div`,{className:`home-world-card-actions`,children:[(0,K.jsx)(`button`,{className:`home-world-rename`,type:`button`,"aria-label":j(`重命名世界:{title}`,{title:e.title}),disabled:s,onClick:()=>{x(e.worldId),C(e.title)},children:j(`重命名`)}),(0,K.jsx)(`button`,{className:`home-world-delete`,type:`button`,"aria-label":j(`删除世界:{title}`,{title:e.title}),disabled:s,onClick:()=>y(e),children:j(`删除`)})]}),b===e.worldId?(0,K.jsxs)(`form`,{className:`home-world-rename-form`,onSubmit:t=>{t.preventDefault();let n=S.trim();n!==``&&n!==e.title&&(v(e,n),x(null))},children:[(0,K.jsxs)(`label`,{children:[(0,K.jsx)(`span`,{children:j(`世界名称`)}),(0,K.jsx)(`input`,{"aria-label":j(`世界名称`),autoFocus:!0,maxLength:160,value:S,onChange:e=>C(e.currentTarget.value)})]}),(0,K.jsxs)(`div`,{className:`button-row`,children:[(0,K.jsx)(`button`,{type:`submit`,"aria-label":j(`保存世界名称`),disabled:S.trim()===``||S.trim()===e.title,children:j(`保存`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,onClick:()=>x(null),children:j(`取消`)})]})]}):null]},e.worldId))}):(0,K.jsxs)(`div`,{className:`home-empty-world`,children:[(0,K.jsx)(`span`,{className:`home-empty-orbit`,"aria-hidden":`true`,children:(0,K.jsx)(`span`,{})}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{children:j(`还没有正在游玩的世界`)}),(0,K.jsx)(`p`,{children:j(`先新建内容包,或上传一份 ZIP,再整理开场与世界文档并创建独立世界。`)})]})]})]}),(0,K.jsxs)(`aside`,{className:`home-surface home-readiness`,"aria-label":j(`工作区状态`),children:[(0,K.jsxs)(`header`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`READY CHECK`}),(0,K.jsx)(`h2`,{children:j(`工作区状态`)})]}),(0,K.jsxs)(`dl`,{className:`home-readiness-list`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${r?`is-ready`:`needs-attention`}`,"aria-hidden":`true`}),j(`模型连接`)]}),(0,K.jsx)(`dd`,{children:i??j(`尚未配置`)})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${a===null?`needs-attention`:`is-ready`}`,"aria-hidden":`true`}),j(`当前预设`)]}),(0,K.jsx)(`dd`,{children:a??j(`尚未选择`)})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${w>0?`is-ready`:`needs-attention`}`,"aria-hidden":`true`}),j(`可用内容包`)]}),(0,K.jsxs)(`dd`,{children:[j(`{count} 份`,{count:w}),T>0&&j(` · {count} 份待修复`,{count:T})]})]})]}),(0,K.jsx)(`p`,{className:`home-readiness-note`,children:j(`内容包是世界模板;创建后的世界会独立保存状态、控制与历史。`)})]})]}),(0,K.jsxs)(`section`,{className:`home-task-section`,"aria-labelledby":`home-tasks-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading home-task-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`WORKBENCH`}),(0,K.jsx)(`h2`,{id:`home-tasks-title`,children:j(`创作工作台`)})]}),(0,K.jsx)(`p`,{children:j(`四个入口各自完成一件事,运行中的世界从上方直接进入。`)})]}),(0,K.jsxs)(`div`,{className:`home-task-grid`,"aria-label":j(`四个任务入口`),children:[(0,K.jsx)(Ni,{index:`01`,title:j(`内容编辑`),description:j(`编辑内容包里的开场、世界文档与控制文件。`),detail:e.length>0?j(`{count} 份内容包`,{count:e.length}):j(`从空白内容包开始`),disabled:s,onClick:u}),(0,K.jsx)(Ni,{index:`02`,title:j(`预设`),description:j(`编辑提示块库、叙事规则、后置请求与扩展资源。`),detail:a??j(`选择预设`),disabled:s,onClick:d}),(0,K.jsx)(Ni,{index:`03`,title:j(`新建世界`),description:j(`从一份可用内容包复制出独立演化的世界。`),detail:w>0?j(`{count} 份可用`,{count:w}):j(`需要可用内容包`),disabled:s,onClick:f}),(0,K.jsx)(Ni,{index:`04`,title:j(`提示词预览`),description:j(`检查真实 role、材料、工具、预算与缓存边界。`),detail:j(r?`模型已就绪`:`需要模型连接`),disabled:s,onClick:p})]})]}),(0,K.jsxs)(`section`,{className:`home-surface home-content-library`,"aria-labelledby":`home-content-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`CONTENT`}),(0,K.jsx)(`h2`,{id:`home-content-title`,children:j(`内容包`)})]}),(0,K.jsx)(`button`,{type:`button`,disabled:s,onClick:m,children:j(`新建内容包`)})]}),(0,K.jsxs)(`div`,{className:`home-content-layout`,children:[(0,K.jsx)(`div`,{children:e.length>0?(0,K.jsx)(`div`,{className:`home-package-list`,children:e.map(e=>(0,K.jsxs)(`button`,{className:`home-package-card`,type:`button`,"aria-label":j(`打开内容包:{name}`,{name:e.displayName}),"aria-pressed":e.localId===n,disabled:s,onClick:()=>g(e.localId),children:[(0,K.jsxs)(`span`,{className:`home-package-card-heading`,children:[(0,K.jsx)(`strong`,{children:e.displayName}),(0,K.jsx)(`span`,{className:`home-package-state ${e.status}`,children:e.status===`usable`?j(`可用`):j(`待修复`)})]}),(0,K.jsx)(`span`,{className:`home-package-card-copy`,children:e.status===`usable`?j(`可编辑、预览并创建新世界`):j(`打开并修复当前树中的问题`)})]},e.localId))}):(0,K.jsxs)(`div`,{className:`home-empty-packages`,children:[(0,K.jsx)(`h3`,{children:j(`从一组人类可读文件开始`)}),(0,K.jsx)(`p`,{children:j(`内容包由 opening.md、world 与 control 组成,可以从空白新建,也可以上传一份内容包 ZIP。`)})]})}),(0,K.jsxs)(`form`,{className:`home-import-card`,onSubmit:e=>{e.preventDefault(),h()},children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`IMPORT`}),(0,K.jsx)(`h3`,{children:j(`导入 ZIP`)}),(0,K.jsx)(`p`,{children:j(`选择本应用导出或遵循相同结构的内容包 ZIP;导入会创建新的本地身份,不覆盖同名内容包。`)})]}),(0,K.jsxs)(`label`,{className:`home-zip-picker`,children:[(0,K.jsx)(`span`,{children:j(`内容包 ZIP`)}),(0,K.jsx)(`input`,{className:`home-zip-picker-input`,type:`file`,accept:`.zip,application/zip,application/x-zip-compressed`,"aria-label":j(`内容包 ZIP 文件`),disabled:s,onChange:e=>c(e.target.files?.[0]??null)},o===null?`empty`:`${o.name}:${o.size}:${o.lastModified}`),(0,K.jsxs)(`span`,{className:`home-zip-picker-control`,children:[(0,K.jsx)(`strong`,{children:j(`选择 ZIP 文件`)}),(0,K.jsx)(`span`,{"aria-live":`polite`,children:o===null?j(`尚未选择`):`${o.name} · ${Pi(o.size)}`})]})]}),(0,K.jsx)(`button`,{type:`submit`,disabled:o===null||s,children:j(s?`正在导入…`:`导入 ZIP`)})]})]})]})]})}function Ni({index:e,title:t,description:n,detail:r,disabled:i,onClick:a}){return(0,K.jsxs)(`button`,{type:`button`,className:`home-task-card`,"aria-labelledby":`home-task-title-${e}`,"aria-describedby":`home-task-description-${e} home-task-detail-${e}`,disabled:i,onClick:a,children:[(0,K.jsx)(`span`,{className:`home-task-index`,children:e}),(0,K.jsx)(`span`,{className:`home-task-arrow`,"aria-hidden":`true`,children:`↗`}),(0,K.jsx)(`strong`,{id:`home-task-title-${e}`,children:t}),(0,K.jsx)(`span`,{className:`home-task-description`,id:`home-task-description-${e}`,children:n}),(0,K.jsx)(`span`,{className:`home-task-detail`,id:`home-task-detail-${e}`,children:r})]})}function Pi(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}function Fi(e){return/\((?:-1|none|auto|minimal|low|medium|high|xhigh|max|\d+)\)$/iu.test(e)}function Ii(){return{connectionId:null,name:j(`默认模型`),presetId:`custom`,provider:`chat_completions`,dialect:`standard`,baseUrl:``,apiKey:``,modelId:``,reasoningEffort:`provider_default`,reasoningSummary:`provider_default`,thinkingMode:`provider_default`,thinkingBudgetTokens:``,contextWindowTokens:`128000`,maxOutputTokens:`16000`}}function Li({client:e,library:t,onLibraryChange:n,onNotice:r,onDirtyChange:i}){let a=t.connections.find(({id:e})=>e===t.activeConnectionId),[o,s]=(0,l.useState)(()=>a===void 0?Ii():zi(a)),[c,u]=(0,l.useState)([]),[d,f]=(0,l.useState)(null),[p,m]=(0,l.useState)(!1),h=t.connections.find(({id:e})=>e===t.activeConnectionId),g=(0,l.useMemo)(()=>t.presets.find(({id:e})=>e===o.presetId),[o.presetId,t.presets]),_=t.connections.find(({id:e})=>e===o.connectionId),v=o.connectionId===null||_?.provider!==o.provider||Vi(_?.baseUrl??``)!==Vi(o.baseUrl);(0,l.useEffect)(()=>i(p),[p,i]),(0,l.useEffect)(()=>()=>{i(!1)},[i]);function y(e,t=!1){s(t=>({...t,...e})),m(!0),t&&u([])}function b(e){let n=t.presets.find(({id:t})=>t===e);n!==void 0&&y(n.id===`custom`?{presetId:e}:{presetId:e,provider:n.provider,dialect:`standard`,...Hi(o,n.provider,`standard`),baseUrl:n.baseUrl,name:o.connectionId===null||o.name===j(`默认模型`)?n.name:o.name},!0)}function x(e){s(zi(e)),u([]),m(!1),r(``)}function S(){s(Ii()),u([]),m(!1),r(``)}async function C(t){if(t.preventDefault(),d===null){f(`save`);try{let t=await e.request({type:`model.save`,connection:{...o.connectionId===null?{}:{connectionId:o.connectionId},name:o.name,presetId:o.presetId,provider:o.provider,dialect:o.dialect,baseUrl:o.baseUrl,...o.apiKey.length===0?{}:{apiKey:o.apiKey},modelId:o.modelId,reasoningEffort:o.reasoningEffort,reasoningSummary:o.reasoningSummary,thinkingMode:o.thinkingMode,thinkingBudgetTokens:o.thinkingMode===`enabled`?Number(o.thinkingBudgetTokens):null,contextWindowTokens:Number(o.contextWindowTokens),maxOutputTokens:Number(o.maxOutputTokens)}});n(t),m(!1);let i=t.connections.find(({id:e})=>e===t.activeConnectionId);i!==void 0&&x(i),r(j(`模型连接已保存并启用;后续请求只使用当前配置。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function w(t){if(d===null){f(t);try{n(await e.request({type:`model.select`,connectionId:t})),r(j(`已切换当前模型配置;Runtime 不会自动故障转移。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function T(t){if(d===null){f(`delete:${t}`);try{n(await e.request({type:`model.delete`,connectionId:t})),o.connectionId===t&&S(),r(j(`模型配置已从本机删除。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function E(){if(d===null){f(`models`);try{let t=await e.request({type:`model.models`,...o.connectionId===null?{}:{connectionId:o.connectionId},provider:o.provider,baseUrl:o.baseUrl,...o.apiKey.length===0?{}:{apiKey:o.apiKey}});u(t.models),r(j(`已从当前端点拉取 {count} 个模型 ID。`,{count:t.models.length}))}catch(e){r(Bi(e))}finally{f(null)}}}return(0,K.jsxs)(`section`,{className:`model-connection-screen`,"aria-labelledby":`model-title`,children:[(0,K.jsxs)(`header`,{className:`config-header`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`eyebrow`,children:`WORKSPACE SETTING`}),(0,K.jsx)(`h2`,{id:`model-title`,children:j(`模型连接`)}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`保存多份本机配置并明确切换。API Key 不会返回浏览器;切换失败时 Runtime 不会悄悄改用另一份配置。`)})]}),(0,K.jsxs)(`div`,{className:`model-active-summary`,children:[(0,K.jsx)(`span`,{children:j(`当前配置`)}),(0,K.jsx)(`strong`,{children:h?.name??j(`尚未启用`)}),(0,K.jsx)(`small`,{children:h?.modelId??j(`先保存一份可用连接`)})]})]}),(0,K.jsxs)(`div`,{className:`config-layout`,children:[(0,K.jsxs)(`form`,{className:`config-form panel-card`,onSubmit:e=>void C(e),children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsx)(`h3`,{children:o.connectionId===null?j(`新建配置`):j(`编辑配置`)}),o.connectionId!==null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:p,onClick:S,type:`button`,children:j(`新建另一份`)})]}),(0,K.jsx)(`label`,{htmlFor:`model-preset`,children:j(`提供商`)}),(0,K.jsx)(`select`,{id:`model-preset`,value:o.presetId,onChange:e=>b(e.target.value),children:t.presets.map(e=>(0,K.jsx)(`option`,{value:e.id,children:e.name},e.id))}),(0,K.jsx)(`label`,{htmlFor:`model-name`,children:j(`配置名称`)}),(0,K.jsx)(`input`,{id:`model-name`,maxLength:160,required:!0,value:o.name,onChange:e=>y({name:e.target.value})}),(0,K.jsx)(`label`,{htmlFor:`model-provider`,children:j(`协议适配器`)}),(0,K.jsxs)(`select`,{id:`model-provider`,value:o.provider,onChange:e=>y({presetId:`custom`,provider:e.target.value,...Hi(o,e.target.value,o.dialect)},!0),children:[(0,K.jsx)(`option`,{value:`chat_completions`,children:`OpenAI-compatible Chat Completions`}),(0,K.jsx)(`option`,{value:`openai_responses`,children:`OpenAI Responses API`}),(0,K.jsx)(`option`,{value:`anthropic_messages`,children:`Anthropic Messages`})]}),(0,K.jsx)(`label`,{htmlFor:`model-dialect`,children:j(`端点方言`)}),(0,K.jsxs)(`select`,{id:`model-dialect`,value:o.dialect,onChange:e=>{let t=e.target.value;y({dialect:t,...Hi(o,o.provider,t)})},children:[(0,K.jsx)(`option`,{value:`standard`,children:j(`协议标准`)}),(0,K.jsx)(`option`,{value:`cliproxyapi`,children:`CLIProxyAPI`})]}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`CLIProxyAPI 方言只启用代理明确支持的兼容参数;响应仍按所选协议解析,不从可见文本猜测思考块。Claude 的签名 thinking 请选 Responses 或 Anthropic Messages;Chat Completions 通常只有 reasoning_content,不能承诺无损续传签名。模型名的 (high) 等 thinking 后缀会覆盖请求参数,因此 Effort 与 Thinking 都必须保留“Provider 默认”。`)}),(0,K.jsxs)(`div`,{className:`model-reasoning-fields`,children:[(0,K.jsxs)(`label`,{htmlFor:`model-reasoning-effort`,children:[j(`Effort(推理强度)`),(0,K.jsxs)(`select`,{id:`model-reasoning-effort`,value:o.reasoningEffort,onChange:e=>y({reasoningEffort:e.target.value}),children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),o.provider===`anthropic_messages`?null:(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`option`,{value:`none`,children:`none`}),(0,K.jsx)(`option`,{value:`minimal`,children:`minimal`})]}),(0,K.jsx)(`option`,{value:`low`,children:`low`}),(0,K.jsx)(`option`,{value:`medium`,children:`medium`}),(0,K.jsx)(`option`,{value:`high`,children:`high`}),(0,K.jsx)(`option`,{value:`xhigh`,children:`xhigh`}),(0,K.jsx)(`option`,{value:`max`,children:`max`})]})]}),(0,K.jsxs)(`label`,{htmlFor:`model-thinking-mode`,children:[j(`Thinking(思考模式)`),(0,K.jsxs)(`select`,{id:`model-thinking-mode`,disabled:o.provider!==`anthropic_messages`,value:o.thinkingMode,onChange:e=>{let t=e.target.value;y({thinkingMode:t,thinkingBudgetTokens:t===`enabled`?o.thinkingBudgetTokens||Wi(o.maxOutputTokens):``,reasoningSummary:t===`provider_default`||t===`disabled`?t===`provider_default`&&Ui({...o,thinkingMode:t})?o.reasoningSummary:`provider_default`:o.reasoningSummary})},children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),(0,K.jsx)(`option`,{value:`adaptive`,children:`adaptive`}),(0,K.jsx)(`option`,{value:`enabled`,children:j(`手动 token 预算`)}),(0,K.jsx)(`option`,{value:`disabled`,children:`disabled`})]})]}),(0,K.jsxs)(`label`,{htmlFor:`model-reasoning-summary`,children:[j(`Thinking 返回内容`),(0,K.jsxs)(`select`,{id:`model-reasoning-summary`,disabled:o.provider===`chat_completions`&&o.dialect===`standard`||o.provider===`anthropic_messages`&&(o.thinkingMode===`provider_default`||o.thinkingMode===`disabled`)&&!Ui(o),value:o.reasoningSummary,onChange:e=>y({reasoningSummary:e.target.value}),children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),(0,K.jsx)(`option`,{value:`auto`,children:`auto`}),o.provider===`openai_responses`?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`option`,{value:`concise`,children:`concise`}),(0,K.jsx)(`option`,{value:`detailed`,children:`detailed`})]}):null,(0,K.jsx)(`option`,{value:`none`,children:`none`})]})]})]}),o.provider===`anthropic_messages`&&o.thinkingMode===`enabled`?(0,K.jsxs)(`label`,{htmlFor:`model-thinking-budget`,children:[j(`Thinking budget tokens`),(0,K.jsx)(`input`,{id:`model-thinking-budget`,min:1024,max:Math.max(1024,Number(o.maxOutputTokens)-1),required:!0,step:1,type:`number`,value:o.thinkingBudgetTokens,onChange:e=>y({thinkingBudgetTokens:e.target.value})})]}):null,(0,K.jsx)(`p`,{className:`field-note`,children:j(`Effort 控制整份响应投入;Anthropic Thinking 独立控制思考块,手动预算必须小于最大输出。Thinking 返回内容只进入模型诊断;原生续传块仍会原样保存。`)}),(0,K.jsx)(`label`,{htmlFor:`model-base-url`,children:`Base URL`}),(0,K.jsx)(`input`,{id:`model-base-url`,"aria-label":`Base URL`,required:!0,type:`url`,value:o.baseUrl,onChange:e=>y({presetId:`custom`,baseUrl:e.target.value},!0)}),g?.id!==`custom`&&(0,K.jsxs)(`p`,{className:`field-note`,children:[j(`已填入`),g?.name,` `,j(`的内置端点;手动修改后按自定义端点保存。`)]}),(0,K.jsx)(`label`,{htmlFor:`model-api-key`,children:`API Key`}),(0,K.jsx)(`input`,{autoComplete:`off`,id:`model-api-key`,"aria-label":`API Key`,required:v,type:`password`,value:o.apiKey,placeholder:o.connectionId===null?j(`新配置必填`):j(v?`端点或协议已改变,必须重新填写`:`留空以保留当前端点的现有凭据`),onChange:e=>y({apiKey:e.target.value},!0)}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`旧凭据只会为同一协议和端点保留,不会静默转发到新端点。`)}),(0,K.jsxs)(`div`,{className:`model-picker-row`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`label`,{htmlFor:`model-id`,children:j(`模型 ID`)}),(0,K.jsx)(`input`,{id:`model-id`,"aria-label":j(`模型 ID`),list:`provider-models`,maxLength:512,required:!0,value:o.modelId,onChange:e=>{let t=e.target.value;y({modelId:t,...o.provider===`anthropic_messages`&&o.thinkingMode===`provider_default`&&!Ui({...o,modelId:t})?{reasoningSummary:`provider_default`}:{}})}}),(0,K.jsx)(`datalist`,{id:`provider-models`,children:c.map(e=>(0,K.jsx)(`option`,{value:e},e))})]}),(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null||o.baseUrl.length===0||v&&o.apiKey.length===0,onClick:()=>void E(),type:`button`,children:j(d===`models`?`正在拉取…`:`从端点拉取模型`)})]}),c.length>0&&(0,K.jsx)(`p`,{className:`field-note`,children:j(`可在模型 ID 输入框中选择已拉取结果。`)}),(0,K.jsxs)(`div`,{className:`two-column-fields`,children:[(0,K.jsxs)(`label`,{children:[j(`真实 context window`),(0,K.jsx)(`input`,{min:4096,max:16777216,required:!0,step:1,type:`number`,value:o.contextWindowTokens,onChange:e=>y({contextWindowTokens:e.target.value})})]}),(0,K.jsxs)(`label`,{children:[j(`最大输出 tokens`),(0,K.jsx)(`input`,{min:256,max:16777215,required:!0,step:1,type:`number`,value:o.maxOutputTokens,onChange:e=>y({maxOutputTokens:e.target.value})})]})]}),(0,K.jsxs)(`div`,{className:`form-actions`,children:[(0,K.jsx)(`button`,{disabled:d!==null,type:`submit`,children:j(d===`save`?`正在保存…`:`保存模型连接并启用`)}),p&&o.connectionId!==null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null,onClick:()=>{let e=t.connections.find(({id:e})=>e===o.connectionId);e!==void 0&&x(e)},type:`button`,children:j(`放弃修改`)}),p&&o.connectionId===null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null,onClick:S,type:`button`,children:j(`重置新配置`)})]})]}),(0,K.jsxs)(`section`,{className:`config-list`,"aria-label":j(`已保存模型配置`),children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsx)(`h3`,{children:j(`已保存配置`)}),(0,K.jsxs)(`span`,{children:[t.connections.length,` `,j(`份`)]})]}),t.connections.length===0&&(0,K.jsx)(`p`,{className:`panel-card`,children:j(`尚未保存模型配置。`)}),t.connections.map(e=>{let n=e.id===t.activeConnectionId;return(0,K.jsxs)(`article`,{className:`config-card panel-card${n?` active-model-config`:``}`,children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`eyebrow`,children:Ri(e.provider)}),(0,K.jsx)(`h3`,{children:e.name})]}),n&&(0,K.jsx)(`span`,{className:`active-config`,children:j(`当前使用`)})]}),(0,K.jsxs)(`dl`,{children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`模型`)}),(0,K.jsx)(`dd`,{children:e.modelId})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`端点`)}),(0,K.jsx)(`dd`,{children:e.baseUrl})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`窗口 / 输出`)}),(0,K.jsxs)(`dd`,{children:[e.contextWindowTokens.toLocaleString(),` /`,` `,e.maxOutputTokens.toLocaleString()]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`方言 / Effort / Thinking`)}),(0,K.jsxs)(`dd`,{children:[e.dialect,` / `,e.reasoningEffort,` /`,` `,e.thinkingMode,e.thinkingBudgetTokens===null?``:` (${e.thinkingBudgetTokens.toLocaleString()} tokens)`,` `,`/ `,e.reasoningSummary]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`凭据`)}),(0,K.jsx)(`dd`,{children:e.hasApiKey?j(`已保存在本机`):j(`未配置`)})]})]}),(0,K.jsxs)(`div`,{className:`form-actions`,children:[!n&&(0,K.jsx)(`button`,{disabled:d!==null,onClick:()=>void w(e.id),type:`button`,children:d===e.id?j(`正在切换…`):j(`切换到此配置`)}),(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null||p||o.connectionId===e.id,onClick:()=>x(e),type:`button`,children:o.connectionId===e.id?j(`正在编辑`):j(`编辑`)}),(0,K.jsx)(`button`,{className:`danger-button`,disabled:d!==null||p||n&&t.connections.length>1,onClick:()=>void T(e.id),type:`button`,children:d===`delete:${e.id}`?j(`正在删除…`):j(`删除`)})]}),n&&t.connections.length>1&&(0,K.jsx)(`p`,{className:`field-note`,children:j(`先切换到另一份配置,才能删除当前配置。`)})]},e.id)})]})]})]})}function Ri(e){switch(e){case`chat_completions`:return`Chat Completions`;case`openai_responses`:return`OpenAI Responses`;case`anthropic_messages`:return`Anthropic Messages`}}function zi(e){return{connectionId:e.id,name:e.name,presetId:e.presetId,provider:e.provider,dialect:e.dialect,baseUrl:e.baseUrl,apiKey:``,modelId:e.modelId,reasoningEffort:e.reasoningEffort,reasoningSummary:e.reasoningSummary,thinkingMode:e.thinkingMode,thinkingBudgetTokens:e.thinkingBudgetTokens===null?``:String(e.thinkingBudgetTokens),contextWindowTokens:String(e.contextWindowTokens),maxOutputTokens:String(e.maxOutputTokens)}}function Bi(e){return e instanceof Error?e.message:j(`模型配置操作失败`)}function Vi(e){return e.trim().replace(/\/+$/u,``)}function Hi(e,t,n){let r=t===`anthropic_messages`&&(e.reasoningEffort===`none`||e.reasoningEffort===`minimal`)?`provider_default`:e.reasoningEffort,i=t===`anthropic_messages`&&e.provider===`anthropic_messages`?e.thinkingMode:`provider_default`,a=Ui({...e,provider:t,dialect:n,thinkingMode:i});return{reasoningEffort:r,reasoningSummary:t!==`openai_responses`&&(e.reasoningSummary===`concise`||e.reasoningSummary===`detailed`)||t===`chat_completions`&&n===`standard`||t===`anthropic_messages`&&(i===`provider_default`||i===`disabled`)&&!a?`provider_default`:e.reasoningSummary,thinkingMode:i,thinkingBudgetTokens:i===`enabled`?e.thinkingBudgetTokens:``}}function Ui(e){return e.provider===`anthropic_messages`&&e.dialect===`cliproxyapi`&&e.thinkingMode===`provider_default`&&Fi(e.modelId)}function Wi(e){let t=Number(e);return!Number.isSafeInteger(t)||t<=1024?`1024`:String(Math.min(8192,t-1))}function Gi(e){let t=`${e.worldId}\0${e.channel}\0${e.key??``}\0${e.output}`,n=e.frontend.renderer;if(e.projection===`append`){let r=`${t}\0append\0${e.recordId}`;return n?.mode===`app`?`${r}\0app\0${n.revision??`builtin`}`:`${r}\0document`}return n?.mode===`app`?`${t}\0app\0${n.revision??`builtin`}`:`${t}\0document`}function Ki(e,t){return t===`application/json`?JSON.stringify(e,null,2):typeof e==`string`?e:JSON.stringify(e)}function qi(e){let t=_a(e.replace(/\r\n?/gu,`
1180
+ `).length}function Mi({contentPackages:e,worlds:t,selectedPackageId:n,modelConfigured:r,activeModelName:i,currentPresetName:a,importArchive:o,importPending:s,onImportArchiveChange:c,onEditContent:u,onOpenPlayPresets:d=()=>void 0,onCreateWorld:f,onOpenPreview:p,onCreatePackage:m,onImportPackage:h,onOpenPackage:g,onOpenWorld:_,onRenameWorld:v,onDeleteWorld:y}){let[b,x]=(0,l.useState)(null),[S,C]=(0,l.useState)(``),w=e.filter(({status:e})=>e===`usable`).length,T=e.length-w;return(0,K.jsxs)(`div`,{className:`home-dashboard`,children:[(0,K.jsxs)(`div`,{className:`home-primary-grid`,children:[(0,K.jsxs)(`section`,{className:`home-surface home-world-library`,"aria-labelledby":`home-worlds-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`PLAY`}),(0,K.jsx)(`h2`,{id:`home-worlds-title`,children:j(`继续游玩`)})]}),(0,K.jsx)(`span`,{className:`home-count`,"aria-label":j(`{count} 个世界`,{count:t.length}),children:t.length.toLocaleString(ie())})]}),t.length>0?(0,K.jsx)(`div`,{className:`home-world-grid`,children:t.map((e,t)=>(0,K.jsxs)(`div`,{className:`home-world-card-shell`,children:[(0,K.jsxs)(`button`,{className:`home-world-card`,type:`button`,"aria-label":j(`打开世界:{title}`,{title:e.title}),disabled:s,onClick:()=>_(e.worldId),children:[(0,K.jsxs)(`span`,{className:`home-world-card-meta`,children:[j(`世界`),String(t+1).padStart(2,`0`)]}),(0,K.jsx)(`strong`,{children:e.title}),(0,K.jsxs)(`span`,{className:`home-world-card-action`,children:[j(`进入世界`),(0,K.jsx)(`span`,{"aria-hidden":`true`,children:`→`})]})]}),(0,K.jsxs)(`div`,{className:`home-world-card-actions`,children:[(0,K.jsx)(`button`,{className:`home-world-rename`,type:`button`,"aria-label":j(`重命名世界:{title}`,{title:e.title}),disabled:s,onClick:()=>{x(e.worldId),C(e.title)},children:j(`重命名`)}),(0,K.jsx)(`button`,{className:`home-world-delete`,type:`button`,"aria-label":j(`删除世界:{title}`,{title:e.title}),disabled:s,onClick:()=>y(e),children:j(`删除`)})]}),b===e.worldId?(0,K.jsxs)(`form`,{className:`home-world-rename-form`,onSubmit:t=>{t.preventDefault();let n=S.trim();n!==``&&n!==e.title&&(v(e,n),x(null))},children:[(0,K.jsxs)(`label`,{children:[(0,K.jsx)(`span`,{children:j(`世界名称`)}),(0,K.jsx)(`input`,{"aria-label":j(`世界名称`),autoFocus:!0,maxLength:160,value:S,onChange:e=>C(e.currentTarget.value)})]}),(0,K.jsxs)(`div`,{className:`button-row`,children:[(0,K.jsx)(`button`,{type:`submit`,"aria-label":j(`保存世界名称`),disabled:S.trim()===``||S.trim()===e.title,children:j(`保存`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,onClick:()=>x(null),children:j(`取消`)})]})]}):null]},e.worldId))}):(0,K.jsxs)(`div`,{className:`home-empty-world`,children:[(0,K.jsx)(`span`,{className:`home-empty-orbit`,"aria-hidden":`true`,children:(0,K.jsx)(`span`,{})}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{children:j(`还没有正在游玩的世界`)}),(0,K.jsx)(`p`,{children:j(`先新建内容包,或上传一份 ZIP,再整理开场与世界文档并创建独立世界。`)})]})]})]}),(0,K.jsxs)(`aside`,{className:`home-surface home-readiness`,"aria-label":j(`工作区状态`),children:[(0,K.jsxs)(`header`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`READY CHECK`}),(0,K.jsx)(`h2`,{children:j(`工作区状态`)})]}),(0,K.jsxs)(`dl`,{className:`home-readiness-list`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${r?`is-ready`:`needs-attention`}`,"aria-hidden":`true`}),j(`模型连接`)]}),(0,K.jsx)(`dd`,{children:i??j(`尚未配置`)})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${a===null?`needs-attention`:`is-ready`}`,"aria-hidden":`true`}),j(`当前预设`)]}),(0,K.jsx)(`dd`,{children:a??j(`尚未选择`)})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsxs)(`dt`,{children:[(0,K.jsx)(`span`,{className:`home-status-mark ${w>0?`is-ready`:`needs-attention`}`,"aria-hidden":`true`}),j(`可用内容包`)]}),(0,K.jsxs)(`dd`,{children:[j(`{count} 份`,{count:w}),T>0&&j(` · {count} 份待修复`,{count:T})]})]})]}),(0,K.jsx)(`p`,{className:`home-readiness-note`,children:j(`内容包是世界模板;创建后的世界会独立保存状态、控制与历史。`)})]})]}),(0,K.jsxs)(`section`,{className:`home-task-section`,"aria-labelledby":`home-tasks-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading home-task-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`WORKBENCH`}),(0,K.jsx)(`h2`,{id:`home-tasks-title`,children:j(`创作工作台`)})]}),(0,K.jsx)(`p`,{children:j(`四个入口各自完成一件事,运行中的世界从上方直接进入。`)})]}),(0,K.jsxs)(`div`,{className:`home-task-grid`,"aria-label":j(`四个任务入口`),children:[(0,K.jsx)(Ni,{index:`01`,title:j(`内容编辑`),description:j(`编辑内容包里的开场、世界文档与控制文件。`),detail:e.length>0?j(`{count} 份内容包`,{count:e.length}):j(`从空白内容包开始`),disabled:s,onClick:u}),(0,K.jsx)(Ni,{index:`02`,title:j(`预设`),description:j(`编辑提示块库、叙事规则、后置请求与扩展资源。`),detail:a??j(`选择预设`),disabled:s,onClick:d}),(0,K.jsx)(Ni,{index:`03`,title:j(`新建世界`),description:j(`从一份可用内容包复制出独立演化的世界。`),detail:w>0?j(`{count} 份可用`,{count:w}):j(`需要可用内容包`),disabled:s,onClick:f}),(0,K.jsx)(Ni,{index:`04`,title:j(`提示词预览`),description:j(`检查真实 role、材料、工具、预算与缓存边界。`),detail:j(r?`模型已就绪`:`需要模型连接`),disabled:s,onClick:p})]})]}),(0,K.jsxs)(`section`,{className:`home-surface home-content-library`,"aria-labelledby":`home-content-title`,children:[(0,K.jsxs)(`header`,{className:`home-section-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`CONTENT`}),(0,K.jsx)(`h2`,{id:`home-content-title`,children:j(`内容包`)})]}),(0,K.jsx)(`button`,{type:`button`,disabled:s,onClick:m,children:j(`新建内容包`)})]}),(0,K.jsxs)(`div`,{className:`home-content-layout`,children:[(0,K.jsx)(`div`,{children:e.length>0?(0,K.jsx)(`div`,{className:`home-package-list`,children:e.map(e=>(0,K.jsxs)(`button`,{className:`home-package-card`,type:`button`,"aria-label":j(`打开内容包:{name}`,{name:e.displayName}),"aria-pressed":e.localId===n,disabled:s,onClick:()=>g(e.localId),children:[(0,K.jsxs)(`span`,{className:`home-package-card-heading`,children:[(0,K.jsx)(`strong`,{children:e.displayName}),(0,K.jsx)(`span`,{className:`home-package-state ${e.status}`,children:e.status===`usable`?j(`可用`):j(`待修复`)})]}),(0,K.jsx)(`span`,{className:`home-package-card-copy`,children:e.status===`usable`?j(`可编辑、预览并创建新世界`):j(`打开并修复当前树中的问题`)})]},e.localId))}):(0,K.jsxs)(`div`,{className:`home-empty-packages`,children:[(0,K.jsx)(`h3`,{children:j(`从一组人类可读文件开始`)}),(0,K.jsx)(`p`,{children:j(`内容包由 opening.md、world 与 control 组成,可以从空白新建,也可以上传一份内容包 ZIP。`)})]})}),(0,K.jsxs)(`form`,{className:`home-import-card`,onSubmit:e=>{e.preventDefault(),h()},children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`home-section-kicker`,children:`IMPORT`}),(0,K.jsx)(`h3`,{children:j(`导入 ZIP`)}),(0,K.jsx)(`p`,{children:j(`选择本应用导出或遵循相同结构的内容包 ZIP;导入会创建新的本地身份,不覆盖同名内容包。`)})]}),(0,K.jsxs)(`label`,{className:`home-zip-picker`,children:[(0,K.jsx)(`span`,{children:j(`内容包 ZIP`)}),(0,K.jsx)(`input`,{className:`home-zip-picker-input`,type:`file`,accept:`.zip,application/zip,application/x-zip-compressed`,"aria-label":j(`内容包 ZIP 文件`),disabled:s,onChange:e=>c(e.target.files?.[0]??null)},o===null?`empty`:`${o.name}:${o.size}:${o.lastModified}`),(0,K.jsxs)(`span`,{className:`home-zip-picker-control`,children:[(0,K.jsx)(`strong`,{children:j(`选择 ZIP 文件`)}),(0,K.jsx)(`span`,{"aria-live":`polite`,children:o===null?j(`尚未选择`):`${o.name} · ${Pi(o.size)}`})]})]}),(0,K.jsx)(`button`,{type:`submit`,disabled:o===null||s,children:j(s?`正在导入…`:`导入 ZIP`)})]})]})]})]})}function Ni({index:e,title:t,description:n,detail:r,disabled:i,onClick:a}){return(0,K.jsxs)(`button`,{type:`button`,className:`home-task-card`,"aria-labelledby":`home-task-title-${e}`,"aria-describedby":`home-task-description-${e} home-task-detail-${e}`,disabled:i,onClick:a,children:[(0,K.jsx)(`span`,{className:`home-task-index`,children:e}),(0,K.jsx)(`span`,{className:`home-task-arrow`,"aria-hidden":`true`,children:`↗`}),(0,K.jsx)(`strong`,{id:`home-task-title-${e}`,children:t}),(0,K.jsx)(`span`,{className:`home-task-description`,id:`home-task-description-${e}`,children:n}),(0,K.jsx)(`span`,{className:`home-task-detail`,id:`home-task-detail-${e}`,children:r})]})}function Pi(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}function Fi(e){return/\((?:-1|none|auto|minimal|low|medium|high|xhigh|max|\d+)\)$/iu.test(e)}function Ii(){return{connectionId:null,name:j(`默认模型`),presetId:`custom`,provider:`chat_completions`,dialect:`standard`,baseUrl:``,apiKey:``,modelId:``,reasoningEffort:`provider_default`,reasoningSummary:`provider_default`,thinkingMode:`provider_default`,thinkingBudgetTokens:``,contextWindowTokens:`128000`,maxOutputTokens:`16000`}}function Li({client:e,library:t,onLibraryChange:n,onNotice:r,onDirtyChange:i}){let a=t.connections.find(({id:e})=>e===t.activeConnectionId),[o,s]=(0,l.useState)(()=>a===void 0?Ii():zi(a)),[c,u]=(0,l.useState)([]),[d,f]=(0,l.useState)(null),[p,m]=(0,l.useState)(!1),h=t.connections.find(({id:e})=>e===t.activeConnectionId),g=(0,l.useMemo)(()=>t.presets.find(({id:e})=>e===o.presetId),[o.presetId,t.presets]),_=o.connectionId===null;(0,l.useEffect)(()=>i(p),[p,i]),(0,l.useEffect)(()=>()=>{i(!1)},[i]);function v(e,t=!1){s(t=>({...t,...e})),m(!0),t&&u([])}function y(e){let n=t.presets.find(({id:t})=>t===e);n!==void 0&&v(n.id===`custom`?{presetId:e}:{presetId:e,provider:n.provider,dialect:`standard`,...Hi(o,n.provider,`standard`),baseUrl:n.baseUrl,name:o.connectionId===null||o.name===j(`默认模型`)?n.name:o.name},!0)}function b(e){s(zi(e)),u([]),m(!1),r(``)}function x(){s(Ii()),u([]),m(!1),r(``)}async function S(t){if(t.preventDefault(),d===null){f(`save`);try{let t=await e.request({type:`model.save`,connection:{...o.connectionId===null?{}:{connectionId:o.connectionId},name:o.name,presetId:o.presetId,provider:o.provider,dialect:o.dialect,baseUrl:o.baseUrl,...o.apiKey.length===0?{}:{apiKey:o.apiKey},modelId:o.modelId,reasoningEffort:o.reasoningEffort,reasoningSummary:o.reasoningSummary,thinkingMode:o.thinkingMode,thinkingBudgetTokens:o.thinkingMode===`enabled`?Number(o.thinkingBudgetTokens):null,contextWindowTokens:Number(o.contextWindowTokens),maxOutputTokens:Number(o.maxOutputTokens)}});n(t),m(!1);let i=t.connections.find(({id:e})=>e===t.activeConnectionId);i!==void 0&&b(i),r(j(`模型连接已保存并启用;后续请求只使用当前配置。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function C(t){if(d===null){f(t);try{n(await e.request({type:`model.select`,connectionId:t})),r(j(`已切换当前模型配置;Runtime 不会自动故障转移。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function w(i){if(d===null){f(`copy:${i.id}`);try{let a=Vi(i.name,t.connections),o=await e.request({type:`model.copy`,connectionId:i.id,name:a}),s=o.library.connections.find(({id:e})=>e===o.copiedConnectionId);if(s===void 0)throw Error(j(`Runtime 没有返回已克隆的模型配置`));n(o.library),b(s),r(j(`已克隆为「{name}」。副本保留本机凭据,但不会切换当前配置。`,{name:s.name}))}catch(e){r(Bi(e))}finally{f(null)}}}async function T(t){if(d===null){f(`delete:${t}`);try{n(await e.request({type:`model.delete`,connectionId:t})),o.connectionId===t&&x(),r(j(`模型配置已从本机删除。`))}catch(e){r(Bi(e))}finally{f(null)}}}async function E(){if(d===null){f(`models`);try{let t=await e.request({type:`model.models`,...o.connectionId===null?{}:{connectionId:o.connectionId},provider:o.provider,baseUrl:o.baseUrl,...o.apiKey.length===0?{}:{apiKey:o.apiKey}});u(t.models),r(j(`已从当前端点拉取 {count} 个模型 ID。`,{count:t.models.length}))}catch(e){r(Bi(e))}finally{f(null)}}}return(0,K.jsxs)(`section`,{className:`model-connection-screen`,"aria-labelledby":`model-title`,children:[(0,K.jsxs)(`header`,{className:`config-header`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`eyebrow`,children:`WORKSPACE SETTING`}),(0,K.jsx)(`h2`,{id:`model-title`,children:j(`模型连接`)}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`保存多份本机配置并明确切换。API Key 不会返回浏览器;切换失败时 Runtime 不会悄悄改用另一份配置。`)})]}),(0,K.jsxs)(`div`,{className:`model-active-summary`,children:[(0,K.jsx)(`span`,{children:j(`当前配置`)}),(0,K.jsx)(`strong`,{children:h?.name??j(`尚未启用`)}),(0,K.jsx)(`small`,{children:h?.modelId??j(`先保存一份可用连接`)})]})]}),(0,K.jsxs)(`div`,{className:`config-layout`,children:[(0,K.jsxs)(`form`,{className:`config-form panel-card`,onSubmit:e=>void S(e),children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsx)(`h3`,{children:o.connectionId===null?j(`新建配置`):j(`编辑配置`)}),o.connectionId!==null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:p,onClick:x,type:`button`,children:j(`新建另一份`)})]}),(0,K.jsx)(`label`,{htmlFor:`model-preset`,children:j(`提供商`)}),(0,K.jsx)(`select`,{id:`model-preset`,value:o.presetId,onChange:e=>y(e.target.value),children:t.presets.map(e=>(0,K.jsx)(`option`,{value:e.id,children:e.name},e.id))}),(0,K.jsx)(`label`,{htmlFor:`model-name`,children:j(`配置名称`)}),(0,K.jsx)(`input`,{id:`model-name`,maxLength:160,required:!0,value:o.name,onChange:e=>v({name:e.target.value})}),(0,K.jsx)(`label`,{htmlFor:`model-provider`,children:j(`协议适配器`)}),(0,K.jsxs)(`select`,{id:`model-provider`,value:o.provider,onChange:e=>v({presetId:`custom`,provider:e.target.value,...Hi(o,e.target.value,o.dialect)},!0),children:[(0,K.jsx)(`option`,{value:`chat_completions`,children:`OpenAI-compatible Chat Completions`}),(0,K.jsx)(`option`,{value:`openai_responses`,children:`OpenAI Responses API`}),(0,K.jsx)(`option`,{value:`anthropic_messages`,children:`Anthropic Messages`})]}),(0,K.jsx)(`label`,{htmlFor:`model-dialect`,children:j(`端点方言`)}),(0,K.jsxs)(`select`,{id:`model-dialect`,value:o.dialect,onChange:e=>{let t=e.target.value;v({dialect:t,...Hi(o,o.provider,t)})},children:[(0,K.jsx)(`option`,{value:`standard`,children:j(`协议标准`)}),(0,K.jsx)(`option`,{value:`cliproxyapi`,children:`CLIProxyAPI`})]}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`CLIProxyAPI 方言只启用代理明确支持的兼容参数;响应仍按所选协议解析,不从可见文本猜测思考块。Claude 的签名 thinking 请选 Responses 或 Anthropic Messages;Chat Completions 通常只有 reasoning_content,不能承诺无损续传签名。模型名的 (high) 等 thinking 后缀会覆盖请求参数,因此 Effort 与 Thinking 都必须保留“Provider 默认”。`)}),(0,K.jsxs)(`div`,{className:`model-reasoning-fields`,children:[(0,K.jsxs)(`label`,{htmlFor:`model-reasoning-effort`,children:[j(`Effort(推理强度)`),(0,K.jsxs)(`select`,{id:`model-reasoning-effort`,value:o.reasoningEffort,onChange:e=>v({reasoningEffort:e.target.value}),children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),o.provider===`anthropic_messages`?null:(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`option`,{value:`none`,children:`none`}),(0,K.jsx)(`option`,{value:`minimal`,children:`minimal`})]}),(0,K.jsx)(`option`,{value:`low`,children:`low`}),(0,K.jsx)(`option`,{value:`medium`,children:`medium`}),(0,K.jsx)(`option`,{value:`high`,children:`high`}),(0,K.jsx)(`option`,{value:`xhigh`,children:`xhigh`}),(0,K.jsx)(`option`,{value:`max`,children:`max`})]})]}),(0,K.jsxs)(`label`,{htmlFor:`model-thinking-mode`,children:[j(`Thinking(思考模式)`),(0,K.jsxs)(`select`,{id:`model-thinking-mode`,disabled:o.provider!==`anthropic_messages`,value:o.thinkingMode,onChange:e=>{let t=e.target.value;v({thinkingMode:t,thinkingBudgetTokens:t===`enabled`?o.thinkingBudgetTokens||Wi(o.maxOutputTokens):``,reasoningSummary:t===`provider_default`||t===`disabled`?t===`provider_default`&&Ui({...o,thinkingMode:t})?o.reasoningSummary:`provider_default`:o.reasoningSummary})},children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),(0,K.jsx)(`option`,{value:`adaptive`,children:`adaptive`}),(0,K.jsx)(`option`,{value:`enabled`,children:j(`手动 token 预算`)}),(0,K.jsx)(`option`,{value:`disabled`,children:`disabled`})]})]}),(0,K.jsxs)(`label`,{htmlFor:`model-reasoning-summary`,children:[j(`Thinking 返回内容`),(0,K.jsxs)(`select`,{id:`model-reasoning-summary`,disabled:o.provider===`chat_completions`&&o.dialect===`standard`||o.provider===`anthropic_messages`&&(o.thinkingMode===`provider_default`||o.thinkingMode===`disabled`)&&!Ui(o),value:o.reasoningSummary,onChange:e=>v({reasoningSummary:e.target.value}),children:[(0,K.jsx)(`option`,{value:`provider_default`,children:j(`Provider 默认`)}),(0,K.jsx)(`option`,{value:`auto`,children:`auto`}),o.provider===`openai_responses`?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`option`,{value:`concise`,children:`concise`}),(0,K.jsx)(`option`,{value:`detailed`,children:`detailed`})]}):null,(0,K.jsx)(`option`,{value:`none`,children:`none`})]})]})]}),o.provider===`anthropic_messages`&&o.thinkingMode===`enabled`?(0,K.jsxs)(`label`,{htmlFor:`model-thinking-budget`,children:[j(`Thinking budget tokens`),(0,K.jsx)(`input`,{id:`model-thinking-budget`,min:1024,max:Math.max(1024,Number(o.maxOutputTokens)-1),required:!0,step:1,type:`number`,value:o.thinkingBudgetTokens,onChange:e=>v({thinkingBudgetTokens:e.target.value})})]}):null,(0,K.jsx)(`p`,{className:`field-note`,children:j(`Effort 控制整份响应投入;Anthropic Thinking 独立控制思考块,手动预算必须小于最大输出。Thinking 返回内容只进入模型诊断;原生续传块仍会原样保存。`)}),(0,K.jsx)(`label`,{htmlFor:`model-base-url`,children:`Base URL`}),(0,K.jsx)(`input`,{id:`model-base-url`,"aria-label":`Base URL`,required:!0,type:`url`,value:o.baseUrl,onChange:e=>v({presetId:`custom`,baseUrl:e.target.value},!0)}),g?.id!==`custom`&&(0,K.jsxs)(`p`,{className:`field-note`,children:[j(`已填入`),g?.name,` `,j(`的内置端点;手动修改后按自定义端点保存。`)]}),(0,K.jsx)(`label`,{htmlFor:`model-api-key`,children:`API Key`}),(0,K.jsx)(`input`,{autoComplete:`off`,id:`model-api-key`,"aria-label":`API Key`,required:_,type:`password`,value:o.apiKey,placeholder:o.connectionId===null?j(`新配置必填`):j(`留空以沿用本机保存的现有凭据`),onChange:e=>v({apiKey:e.target.value},!0)}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`编辑已有配置时,API Key 留空会把本机保存的现有凭据用于当前协议和端点;填写新值会覆盖它。`)}),(0,K.jsxs)(`div`,{className:`model-picker-row`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`label`,{htmlFor:`model-id`,children:j(`模型 ID`)}),(0,K.jsx)(`input`,{id:`model-id`,"aria-label":j(`模型 ID`),list:`provider-models`,maxLength:512,required:!0,value:o.modelId,onChange:e=>{let t=e.target.value;v({modelId:t,...o.provider===`anthropic_messages`&&o.thinkingMode===`provider_default`&&!Ui({...o,modelId:t})?{reasoningSummary:`provider_default`}:{}})}}),(0,K.jsx)(`datalist`,{id:`provider-models`,children:c.map(e=>(0,K.jsx)(`option`,{value:e},e))})]}),(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null||o.baseUrl.length===0||_&&o.apiKey.length===0,onClick:()=>void E(),type:`button`,children:j(d===`models`?`正在拉取…`:`从端点拉取模型`)})]}),c.length>0&&(0,K.jsx)(`p`,{className:`field-note`,children:j(`可在模型 ID 输入框中选择已拉取结果。`)}),(0,K.jsxs)(`div`,{className:`two-column-fields`,children:[(0,K.jsxs)(`label`,{children:[j(`真实 context window`),(0,K.jsx)(`input`,{min:4096,max:16777216,required:!0,step:1,type:`number`,value:o.contextWindowTokens,onChange:e=>v({contextWindowTokens:e.target.value})})]}),(0,K.jsxs)(`label`,{children:[j(`最大输出 tokens`),(0,K.jsx)(`input`,{min:256,max:16777215,required:!0,step:1,type:`number`,value:o.maxOutputTokens,onChange:e=>v({maxOutputTokens:e.target.value})})]})]}),(0,K.jsxs)(`div`,{className:`form-actions`,children:[(0,K.jsx)(`button`,{disabled:d!==null,type:`submit`,children:j(d===`save`?`正在保存…`:`保存模型连接并启用`)}),p&&o.connectionId!==null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null,onClick:()=>{let e=t.connections.find(({id:e})=>e===o.connectionId);e!==void 0&&b(e)},type:`button`,children:j(`放弃修改`)}),p&&o.connectionId===null&&(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null,onClick:x,type:`button`,children:j(`重置新配置`)})]})]}),(0,K.jsxs)(`section`,{className:`config-list`,"aria-label":j(`已保存模型配置`),children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsx)(`h3`,{children:j(`已保存配置`)}),(0,K.jsxs)(`span`,{children:[t.connections.length,` `,j(`份`)]})]}),t.connections.length===0&&(0,K.jsx)(`p`,{className:`panel-card`,children:j(`尚未保存模型配置。`)}),t.connections.map(e=>{let n=e.id===t.activeConnectionId;return(0,K.jsxs)(`article`,{className:`config-card panel-card${n?` active-model-config`:``}`,children:[(0,K.jsxs)(`div`,{className:`section-heading-row`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`eyebrow`,children:Ri(e.provider)}),(0,K.jsx)(`h3`,{children:e.name})]}),n&&(0,K.jsx)(`span`,{className:`active-config`,children:j(`当前使用`)})]}),(0,K.jsxs)(`dl`,{children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`模型`)}),(0,K.jsx)(`dd`,{children:e.modelId})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`端点`)}),(0,K.jsx)(`dd`,{children:e.baseUrl})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`窗口 / 输出`)}),(0,K.jsxs)(`dd`,{children:[e.contextWindowTokens.toLocaleString(),` /`,` `,e.maxOutputTokens.toLocaleString()]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`方言 / Effort / Thinking`)}),(0,K.jsxs)(`dd`,{children:[e.dialect,` / `,e.reasoningEffort,` /`,` `,e.thinkingMode,e.thinkingBudgetTokens===null?``:` (${e.thinkingBudgetTokens.toLocaleString()} tokens)`,` `,`/ `,e.reasoningSummary]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`dt`,{children:j(`凭据`)}),(0,K.jsx)(`dd`,{children:e.hasApiKey?j(`已保存在本机`):j(`未配置`)})]})]}),(0,K.jsxs)(`div`,{className:`form-actions`,children:[!n&&(0,K.jsx)(`button`,{disabled:d!==null,onClick:()=>void C(e.id),type:`button`,children:d===e.id?j(`正在切换…`):j(`切换到此配置`)}),(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null||p,onClick:()=>void w(e),type:`button`,children:d===`copy:${e.id}`?j(`正在克隆…`):j(`克隆配置`)}),(0,K.jsx)(`button`,{className:`secondary-button`,disabled:d!==null||p||o.connectionId===e.id,onClick:()=>b(e),type:`button`,children:o.connectionId===e.id?j(`正在编辑`):j(`编辑`)}),(0,K.jsx)(`button`,{className:`danger-button`,disabled:d!==null||p||n&&t.connections.length>1,onClick:()=>void T(e.id),type:`button`,children:d===`delete:${e.id}`?j(`正在删除…`):j(`删除`)})]}),n&&t.connections.length>1&&(0,K.jsx)(`p`,{className:`field-note`,children:j(`先切换到另一份配置,才能删除当前配置。`)})]},e.id)})]})]})]})}function Ri(e){switch(e){case`chat_completions`:return`Chat Completions`;case`openai_responses`:return`OpenAI Responses`;case`anthropic_messages`:return`Anthropic Messages`}}function zi(e){return{connectionId:e.id,name:e.name,presetId:e.presetId,provider:e.provider,dialect:e.dialect,baseUrl:e.baseUrl,apiKey:``,modelId:e.modelId,reasoningEffort:e.reasoningEffort,reasoningSummary:e.reasoningSummary,thinkingMode:e.thinkingMode,thinkingBudgetTokens:e.thinkingBudgetTokens===null?``:String(e.thinkingBudgetTokens),contextWindowTokens:String(e.contextWindowTokens),maxOutputTokens:String(e.maxOutputTokens)}}function Bi(e){return e instanceof Error?e.message:j(`模型配置操作失败`)}function Vi(e,t){let n=new Set(t.map(({name:e})=>e));for(let r=1;r<=t.length+1;r+=1){let t=r===1?j(`(副本)`):j(`(副本 {number})`,{number:r}),i=`${e.slice(0,Math.max(0,160-t.length))}${t}`;if(!n.has(i))return i}throw Error(j(`无法生成不重复的副本名称`))}function Hi(e,t,n){let r=t===`anthropic_messages`&&(e.reasoningEffort===`none`||e.reasoningEffort===`minimal`)?`provider_default`:e.reasoningEffort,i=t===`anthropic_messages`&&e.provider===`anthropic_messages`?e.thinkingMode:`provider_default`,a=Ui({...e,provider:t,dialect:n,thinkingMode:i});return{reasoningEffort:r,reasoningSummary:t!==`openai_responses`&&(e.reasoningSummary===`concise`||e.reasoningSummary===`detailed`)||t===`chat_completions`&&n===`standard`||t===`anthropic_messages`&&(i===`provider_default`||i===`disabled`)&&!a?`provider_default`:e.reasoningSummary,thinkingMode:i,thinkingBudgetTokens:i===`enabled`?e.thinkingBudgetTokens:``}}function Ui(e){return e.provider===`anthropic_messages`&&e.dialect===`cliproxyapi`&&e.thinkingMode===`provider_default`&&Fi(e.modelId)}function Wi(e){let t=Number(e);return!Number.isSafeInteger(t)||t<=1024?`1024`:String(Math.min(8192,t-1))}function Gi(e){let t=`${e.worldId}\0${e.channel}\0${e.key??``}\0${e.output}`,n=e.frontend.renderer;if(e.projection===`append`){let r=`${t}\0append\0${e.recordId}`;return n?.mode===`app`?`${r}\0app\0${n.revision??`builtin`}`:`${r}\0document`}return n?.mode===`app`?`${t}\0app\0${n.revision??`builtin`}`:`${t}\0document`}function Ki(e,t){return t===`application/json`?JSON.stringify(e,null,2):typeof e==`string`?e:JSON.stringify(e)}function qi(e){let t=_a(e.replace(/\r\n?/gu,`
1181
1181
  `)).split(`
1182
1182
  `),n=[],r=!1;for(let e of t){let t=/^(#{1,6})\s+(.*)$/u.exec(e),i=/^[-*]\s+(.*)$/u.exec(e);if(i!==null){r||=(n.push(`<ul>`),!0),n.push(`<li>${ga(i[1]??``)}</li>`);continue}if(r&&=(n.push(`</ul>`),!1),t!==null){let e=t[1]?.length??1;n.push(`<h${e}>${ga(t[2]??``)}</h${e}>`)}else e.trim()!==``&&n.push(`<p>${ga(e)}</p>`)}return r&&n.push(`</ul>`),n.join(`
1183
1183
  `)}function Ji(e){let t=e.maxRules??256,n=e.maxTransformations??1024,r=e.maxOutputBytes??4194304,i=e.maxDomCharacters??1e6,a=Ki(e.payload,e.contentType),o=e.contentType===`text/markdown`?qi(a):``;if(new TextEncoder().encode(a).byteLength>r||o.length>i)return{original:a,final:a,steps:[],fallback:!0,failure:`fallback`,error:j(`artifact 内容超过宿主渲染上限`)};if(e.rules.length>t)return{original:a,final:a,steps:[],fallback:!0,failure:`fallback`,error:j(`正则规则数量超过宿主上限`)};let s=[...e.rules].sort((e,t)=>e.order-t.order),c=a,l=e.contentType===`application/json`?a:c,u=e.contentType===`text/markdown`?o:c,d=!1,f=[],p=0,m=`none`;for(let t of s){let o=t.scope===`raw_text`?c:t.scope===`structured_payload`?l:u,s=0;try{if(t.scope===`markdown_html`&&e.contentType!==`text/markdown`){f.push({order:t.order,scope:t.scope,input:o,output:o,matches:0,status:`skipped`,error:j(`markdown_html 规则只作用于 Markdown 转换后的 HTML`)});continue}let a=new RegExp(t.pattern,t.flags.includes(`g`)?t.flags:`${t.flags}g`),m=o.replace(a,(...e)=>s>=t.maxMatches?typeof e[0]==`string`?e[0]:``:(s+=1,p+=1,ha(t.replace,e,o)));if(p>n)throw Error(j(`正则转换次数超过宿主上限`));if(new TextEncoder().encode(m).byteLength>r)throw Error(j(`正则输出超过宿主字节上限`));if(t.scope===`markdown_html`&&m.length>i)throw Error(j(`Markdown HTML DOM 大小超过宿主上限`));if(t.scope===`raw_text`){if(c=m,!d&&e.contentType===`text/markdown`&&(u=qi(c),u.length>i))throw Error(j(`Markdown HTML DOM 大小超过宿主上限`))}else t.scope===`structured_payload`?l=m:(u=m,d=!0);f.push({order:t.order,scope:t.scope,input:o,output:m,matches:s,status:`applied`})}catch(e){let n=e instanceof Error?e.message:j(`正则执行失败`);if(t.errorPolicy===`skip`)f.push({order:t.order,scope:t.scope,input:o,output:o,matches:s,status:`skipped`,error:n});else if(t.errorPolicy===`fallback`){f.push({order:t.order,scope:t.scope,input:o,output:a,matches:s,status:`fallback`,error:n}),m=`fallback`;break}else{f.push({order:t.order,scope:t.scope,input:o,output:o,matches:s,status:`failed`,error:n}),m=`fail`;break}}}return{original:a,final:m===`none`?e.contentType===`text/markdown`?u:e.contentType===`application/json`?l:c:a,steps:f,fallback:m!==`none`,failure:m,...m===`fallback`?{error:j(`正则规则失败,实例显示 raw artifact(可恢复)`)}:m===`fail`?{error:j(`正则规则失败,实例进入错误状态并显示 raw artifact`)}:{}}}function Yi(e,t,n,r){return n?.mode===`document`&&n.document!==void 0?Xi({template:n.document,content:e,contentType:t,...r===void 0?{}:{payload:r}}):t===`text/html`?e:`<!doctype html><meta charset="utf-8"><style>body{font:15px/1.6 system-ui,sans-serif;margin:1rem;color:#202020}pre{white-space:pre-wrap}code{font-family:ui-monospace,monospace}</style><main>${t===`text/markdown`?e:`<pre>${_a(e)}</pre>`}</main>`}function Xi(e){let t=`<!-- narraeon:content -->`,n=`<!-- narraeon:payload -->`;if(ea(e.template,t)!==1)throw Error(j(`document renderer 必须包含唯一 narraeon:content marker`));let r=e.contentType===`text/html`||e.contentType===`text/markdown`?e.content:`<pre>${_a(e.content)}</pre>`,i=e.template.replace(t,r);if(i.includes(n)){let t=JSON.stringify(e.payload??null).replace(/</gu,`\\u003c`);i=i.replace(n,`<script type="application/json" data-narraeon-payload>${t}<\/script>`)}return i}function Zi(e){let t=e.renderer.document??`<main></main>`,n=e.renderer.scripts.map(e=>`<script>${e.replace(/<\/script/giu,`<\\/script`)}<\/script>`).join(``),r=JSON.stringify(Object.fromEntries(e.renderer.assets.map(({id:e,source:t})=>[e,t]))).replace(/</gu,`\\u003c`),i=JSON.stringify(e.instanceId),a=JSON.stringify(e.nonce),o=`${`<script>(function(){var send=function(message){parent.postMessage(message,"*")};window.addEventListener("error",function(event){send({namespace:"${ta}",type:"bridge.event",instanceId:${i},nonce:${a},event:"diagnostic",payload:{message:String(event.message||"Extension script error")}})});window.addEventListener("unhandledrejection",function(event){send({namespace:"${ta}",type:"bridge.event",instanceId:${i},nonce:${a},event:"diagnostic",payload:{message:String(event.reason||"Extension Promise error")}})})})();<\/script>`}${`<meta name="narraeon-extension" content="local-trusted"><script>document.documentElement.dataset.narraeonInstance=${i};document.documentElement.dataset.narraeonNonce=${a};window.__NARRAEON_ASSETS__=${r};<\/script>`}`,s=`<script>parent.postMessage({namespace:"${ta}",type:"bridge.ready",instanceId:${i},nonce:${a}},"*");<\/script>`;return Qi($i(t,o,``),`${n}${s}`)}function Qi(e,t){let n=/<\/body\s*>/iu.exec(e);if(n===null)return`${e}${t}`;let r=n.index??e.length;return`${e.slice(0,r)}${t}${e.slice(r)}`}function $i(e,t,n){let r=/<head(?:\s[^>]*)?>/iu.exec(e);if(r!==null){let i=(r.index??0)+r[0].length;return`${e.slice(0,i)}${t}${n}${e.slice(i)}`}let i=/<html(?:\s[^>]*)?>/iu.exec(e);if(i!==null){let r=(i.index??0)+i[0].length;return`${e.slice(0,r)}<head>${t}${n}</head>${e.slice(r)}`}return`<!doctype html><html><head>${t}${n}</head><body>${e}</body></html>`}function ea(e,t){return e.split(t).length-1}var ta=`narraeon.extension.v1`;function na(e,t){if(typeof e!=`object`||!e)return!1;let n=e;return n.namespace===`narraeon.extension.v1`&&typeof n.command==`string`&&[`read_channel`,`read_player_view`,`composer.set_draft`,`panel.close`,`panel.refresh`,`diagnostic`].includes(n.command)&&n.instanceId===t.instanceId&&n.nonce===t.nonce&&([`read_channel`,`read_player_view`,`composer.set_draft`,`panel.close`,`panel.refresh`].includes(n.command)?typeof n.requestId==`string`&&n.requestId.length>0:n.requestId===void 0||typeof n.requestId==`string`)}function ra(e,t){if(typeof e!=`object`||!e)return!1;let n=e;return n.namespace===`narraeon.extension.v1`&&n.type===`bridge.ready`&&n.instanceId===t.instanceId&&n.nonce===t.nonce}function ia(e,t){if(typeof e!=`object`||!e)return!1;let n=e;return n.namespace===`narraeon.extension.v1`&&n.type===`bridge.event`&&n.event===`diagnostic`&&n.instanceId===t.instanceId&&n.nonce===t.nonce}var aa=[`story`,`sidebar`,`composer_above`,`composer_below`,`overlay`,`debug`],oa=(0,l.createContext)(null);function sa({worldId:e,artifacts:t,playerViewPanels:n=[],playerViews:r,interactionDisabled:i=!1,onSetComposerDraft:a,onRefresh:o,onBridgeEvent:s,children:c}){let[u,d]=(0,l.useState)(()=>new Set),[f,p]=(0,l.useState)(()=>new Map),[m,h]=(0,l.useState)(()=>new Map),g=(0,l.useMemo)(()=>{let e=new Map;for(let t of aa)e.set(t,[]);let r=[...t,...n.map(la)];for(let t of r){let n=t.frontend.mount??(t.frontend.fallback===`raw`?`debug`:void 0);n!==void 0&&(t.projection!==`hidden`||n===`debug`)&&e.get(n)?.push(t)}return e},[t,n]),_=(0,l.useMemo)(()=>({worldId:e,artifacts:t,playerViewPanels:n,playerViews:r,interactionDisabled:i,onSetComposerDraft:a,onRefresh:o,...s===void 0?{}:{onBridgeEvent:s},children:c,byMount:g,disabled:u,errors:f,bridgeEvents:m,disable:e=>d(t=>new Set(t).add(e)),enable:e=>{d(t=>{let n=new Set(t);return n.delete(e),n}),p(t=>{let n=new Map(t);return n.delete(e),n})},error:(e,t)=>p(n=>new Map(n).set(e,t)),bridgeEvent:(e,t,n)=>{h(t=>{let r=new Map(t);return r.set(e,[...r.get(e)??[],n]),r}),s?.(t,n)}}),[t,m,g,c,u,f,s,o,a,r,n,i,e]);return(0,K.jsx)(oa.Provider,{value:_,children:c})}function ca({mount:e}){let t=(0,l.useContext)(oa);if(t===null)throw Error(j(`ArtifactExtensionMount 必须位于 ArtifactExtensionHost 内`));let n=t.byMount.get(e)??[];return(0,K.jsx)(`div`,{className:`artifact-extension-mount artifact-extension-mount-${e}`,"data-extension-mount":e,children:n.map(e=>{let n=Gi(e);return(0,K.jsx)(ua,{artifact:e,worldId:t.worldId,channelArtifacts:t.artifacts.filter(({channel:t})=>t===e.channel),playerViews:t.playerViews.views,interactionDisabled:t.interactionDisabled,disabled:t.disabled.has(n),error:t.errors.get(n),events:t.bridgeEvents.get(n)??[],onDisable:()=>t.disable(n),onEnable:()=>t.enable(n),onError:e=>t.error(n,e),onBridgeEvent:r=>t.bridgeEvent(n,e.recordId,r),onSetComposerDraft:t.onSetComposerDraft,onRefresh:t.onRefresh},n)})})}function la(e){return{recordId:`player-view:${e.panelId}`,worldId:e.worldId,operationId:`player-view:${e.panelId}`,playPresetId:e.preset.id,playPresetRevision:e.preset.revision,requestId:`player_view`,requestAttempt:0,output:e.panelId,channel:e.channel,key:e.key,contentType:e.contentType,payload:e.payload,projection:e.projection,save:`none`,sequence:0,head:e.head,frontend:e.frontend}}function ua({artifact:e,worldId:t,channelArtifacts:n,playerViews:r,interactionDisabled:i,disabled:a,error:o,events:s,onDisable:c,onEnable:u,onError:d,onBridgeEvent:f,onSetComposerDraft:p,onRefresh:m}){let h=`extension-${va(Gi(e))}`,[g,_]=(0,l.useState)(()=>({epoch:0,nonce:ya()})),v=g.nonce,[y,b]=(0,l.useState)(!1),x=(0,l.useMemo)(()=>Ji({payload:e.payload,contentType:e.contentType,rules:e.frontend.regex}),[e.contentType,e.frontend.regex,e.payload]),S=(0,l.useRef)(null),C=e.frontend.renderer,w=C?.mode===`app`,T=x.final,E=(0,l.useMemo)(()=>{try{return{srcDoc:w&&C!==void 0?Zi({renderer:C,instanceId:h,nonce:v}):Yi(T,e.contentType,C,e.payload),error:void 0}}catch(e){return{srcDoc:``,error:e instanceof Error?e.message:j(`文档 renderer 无效`)}}},[e.contentType,e.payload,T,h,w,v,C]),D=E.srcDoc,O=E.error,ee=o??O??(x.failure===`fail`?x.error:void 0),k=(0,l.useCallback)(()=>{b(!1),_(e=>({epoch:e.epoch+1,nonce:ya()})),m()},[m]),te=(0,l.useCallback)(()=>{let n=S.current;!w||n?.contentWindow==null||n.contentWindow.postMessage({namespace:ta,type:`render.update`,instanceId:h,nonce:v,payload:{content:T,contentType:e.contentType,worldId:t,channel:e.channel,key:e.key,interactionDisabled:i}},`*`)},[e.channel,e.contentType,e.key,T,h,w,v,t,i]);(0,l.useEffect)(()=>{function e(e){if(e.source!==S.current?.contentWindow)return;if(ra(e.data,{instanceId:h,nonce:v})){b(!0),f(`bridge.ready`);return}if(ia(e.data,{instanceId:h,nonce:v})){let t=ba(e.data.payload)?e.data.payload:{};f(`diagnostic`),d(typeof t.message==`string`?t.message:j(`扩展报告了未知诊断`));return}if(!na(e.data,{instanceId:h,nonce:v}))return;f(e.data.command);let t=ba(e.data.payload)?e.data.payload:{};if(e.data.command===`read_channel`)da(e,e.data,{artifacts:n.map(e=>({recordId:e.recordId,channel:e.channel,...e.key===void 0?{}:{key:e.key},contentType:e.contentType,payload:e.payload,projection:e.projection,save:e.save,sequence:e.sequence,head:e.head}))});else if(e.data.command===`read_player_view`){let n=typeof t.viewId==`string`?t.viewId:``;da(e,e.data,{view:r.find(e=>ba(e)&&e.id===n)})}else e.data.command===`composer.set_draft`?i?pa(e,e.data,j(`当前调用链正在提交,暂不能修改草稿`)):typeof t.text==`string`?(p(t.text),fa(e,e.data,{accepted:!0})):pa(e,e.data,j(`composer.set_draft.text 必须是字符串`)):e.data.command===`panel.close`?(c(),da(e,e.data,{accepted:!0})):e.data.command===`panel.refresh`?(k(),da(e,e.data,{accepted:!0})):e.data.command===`diagnostic`&&d(typeof t.message==`string`?t.message:j(`扩展报告了未知诊断`))}return window.addEventListener(`message`,e),()=>window.removeEventListener(`message`,e)},[n,h,v,f,c,d,m,p,i,r,k]),(0,l.useEffect)(()=>{y&&te()},[y,te]);function ne(){b(!0),w&&window.setTimeout(te,0)}return(0,K.jsxs)(`article`,{className:`artifact-extension-instance${ee?` has-error`:``}`,"data-artifact-output":e.output,"data-artifact-status":e.frontend.status,"data-extension-authority":e.frontend.authority??`non_authoritative_artifact`,children:[a||ee||e.frontend.fallback===`raw`||x.fallback?(0,K.jsxs)(`div`,{className:`artifact-extension-fallback`,role:`status`,children:[(0,K.jsx)(`p`,{children:ee??x.error??e.frontend.diagnostic??j(`扩展资源不可用,显示 raw artifact。`)}),(0,K.jsx)(`pre`,{children:x.original}),(0,K.jsx)(`button`,{type:`button`,onClick:a?u:c,children:j(a?`恢复此扩展`:`停用此扩展`)})]}):(0,K.jsxs)(K.Fragment,{children:[e.frontend.trustedLocalCode?(0,K.jsx)(`small`,{className:`artifact-extension-trusted-label`,children:j(`本地可信代码`)}):null,(0,K.jsx)(`iframe`,{ref:S,title:e.output,className:`artifact-extension-frame`,sandbox:e.frontend.trustedLocalCode?`allow-scripts`:``,srcDoc:D,onLoad:ne,onError:()=>d(j(`扩展 iframe 加载失败`))},g.epoch)]}),e.frontend.mount===`debug`?(0,K.jsxs)(`details`,{className:`artifact-extension-debug`,children:[(0,K.jsx)(`summary`,{children:j(`产物诊断`)}),(0,K.jsxs)(`dl`,{children:[(0,K.jsx)(`dt`,{children:j(`来源`)}),(0,K.jsxs)(`dd`,{children:[e.operationId,` / `,e.requestId,` / attempt`,` `,e.requestAttempt]}),(0,K.jsx)(`dt`,{children:j(`保存/投影`)}),(0,K.jsxs)(`dd`,{children:[e.save,` / `,e.projection]}),(0,K.jsx)(`dt`,{children:`renderer revision`}),(0,K.jsx)(`dd`,{children:e.frontend.renderer?.revision??`built-in`})]}),(0,K.jsx)(`p`,{children:`raw payload`}),(0,K.jsx)(`pre`,{children:x.original}),(0,K.jsx)(`p`,{children:`regex steps`}),(0,K.jsx)(`pre`,{children:JSON.stringify(x.steps,null,2)}),s.length===0?null:(0,K.jsxs)(`p`,{children:[`bridge events: `,s.join(`, `)]})]}):null,s.length===0?null:(0,K.jsxs)(`details`,{className:`artifact-extension-events`,children:[(0,K.jsxs)(`summary`,{children:[j(`bridge 事件(`),s.length,`)`]}),(0,K.jsx)(`ul`,{children:s.map((e,t)=>(0,K.jsx)(`li`,{children:e},`${e}-${t}`))})]})]})}function da(e,t,n){fa(e,t,n)}function fa(e,t,n){ma(e,t,{ok:!0,payload:n})}function pa(e,t,n){ma(e,t,{ok:!1,error:n})}function ma(e,t,n){if(t.requestId===void 0)return;let r=e.source;r!==null&&`postMessage`in r&&r.postMessage({namespace:ta,type:`bridge.response`,ok:n.ok,requestId:t.requestId,instanceId:t.instanceId,nonce:t.nonce,...n.ok?{payload:n.payload}:{error:n.error}},`*`)}function ha(e,t,n){let r=typeof t[0]==`string`?t[0]:``,i=typeof t.at(-2)==`number`?t.at(-2):0,a=typeof t.at(-1)==`object`?-3:-2,o=t.slice(1,a);return e.replace(/\$(\$|&|`|'|\d{1,2})/gu,(e,t)=>{if(t===`&`)return r;if(t==="`")return n.slice(0,i);if(t===`'`)return n.slice(i+r.length);if(/^\d{1,2}$/u.test(t)){let e=o[Number(t)-1];return typeof e==`string`?e:``}return`$`})}function ga(e){return e.replace(/`([^`]+)`/gu,`<code>$1</code>`).replace(/\*\*([^*]+)\*\*/gu,`<strong>$1</strong>`)}function _a(e){return e.replace(/&/gu,`&amp;`).replace(/</gu,`&lt;`).replace(/>/gu,`&gt;`).replace(/"/gu,`&quot;`).replace(/'/gu,`&#39;`)}function va(e){let t=2166136261;for(let n of e)t^=n.codePointAt(0)??0,t=Math.imul(t,16777619);return(t>>>0).toString(36)}function ya(){return ee(`extension-nonce`)}function ba(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var xa=[{id:`call_chain`,label:`调用链`,description:`叙事规则、后置请求与工具契约`},{id:`setting_improvement`,label:`设定完善`,description:`AI 创作方法;工具契约保持内置`},{id:`extensions`,label:`界面扩展`,description:`频道挂载、面板与扩展引用`},{id:`blocks`,label:`提示内容`,description:`阅读、编辑并排序主持规则块`},{id:`files`,label:`高级文件`,description:`带用途说明的完整源文件`},{id:`preview`,label:`产物预览`,description:`冻结 contract 与 renderer 的真实预览`}];function Sa(e){let t=structuredClone(e);if(t.draft===void 0)return t;let n={...t,revision:t.draft.revision,files:structuredClone(t.draft.files),validation:structuredClone(t.draft.validation)};return t.draft.structure===void 0?delete n.structure:n.structure=structuredClone(t.draft.structure),n}function Ca(e){return Object.prototype.hasOwnProperty.call(e,`preset.yaml`)?`preset.yaml`:Object.prototype.hasOwnProperty.call(e,`call-chain.yaml`)?`call-chain.yaml`:Object.keys(e).sort()[0]??``}function wa({client:e,initialLibrary:t,recommendedTemplates:n=[],onLibraryChange:r,onDirtyChange:i,renderPromptPreview:a}){let o=C(ie()),[s,c]=(0,l.useState)(t),u=t.presets.find(({id:e})=>e===t.currentPresetId)??t.presets[0],[d,f]=(0,l.useState)(u?.id??``),[p,m]=(0,l.useState)(()=>u===void 0?null:Sa(u)),[h,g]=(0,l.useState)(u?.files===void 0?``:Ca(u.draft?.files??u.files)),[_,v]=(0,l.useState)(``),[y,b]=(0,l.useState)(``),[x,S]=(0,l.useState)(`call_chain`),[w,T]=(0,l.useState)(!1),[E,D]=(0,l.useState)(null),[O,ee]=(0,l.useState)(null),[k,te]=(0,l.useState)(!1),[ne,A]=(0,l.useState)(null),re=(0,l.useRef)(0),M=(0,l.useMemo)(()=>s.presets.find(({id:e})=>e===p?.id),[p?.id,s.presets]),N=M?.draft??M,ae=p!==null&&N!==void 0&&(JSON.stringify(p.files)!==JSON.stringify(N.files)||JSON.stringify(p.structure)!==JSON.stringify(N.structure)),P=p!==null&&M!==void 0&&p.name!==M.name,F=p!==null&&M!==void 0&&(P||ae),I=(0,l.useMemo)(()=>{let e=p?.structure?.callChainPath??N?.structure?.callChainPath??`call-chain.yaml`;return new Set([`preset.yaml`,e])},[p?.structure?.callChainPath,N?.structure?.callChainPath]),L=p!==null&&N!==void 0&&[...I].some(e=>p.files[e]!==N.files[e]),R=p!==null&&N!==void 0&&JSON.stringify(p.structure)!==JSON.stringify(N.structure),oe=L&&R,se=s.presets.find(({id:e})=>e===s.currentPresetId),z=p?.structure?.followups.length??0,B=p?.structure?.followups.reduce((e,t)=>e+t.artifacts.length,0)??0,ce=p?.structure?.mounts.length??0,V=p===null?0:Object.keys(p.files).length;(0,l.useEffect)(()=>{i(F)},[F,i]),(0,l.useEffect)(()=>()=>i(!1),[i]),(0,l.useEffect)(()=>{let t=p,n=++re.current;if(t?.validation.status!==`valid`||t?.id!==d||F)return;let r=!0;return queueMicrotask(()=>{r&&n===re.current&&te(!0)}),Promise.resolve(e.request({type:`play.workbench.read`,presetId:t.id,revision:t.revision})).then(e=>{e!==void 0&&r&&n===re.current&&e.id===t.id&&e.revision===t.revision&&ee(e)}).catch(()=>{r&&n===re.current&&ee(null)}).finally(()=>{r&&n===re.current&&te(!1)}),()=>{r=!1}},[e,F,p,d]);let H=p!==null&&p.validation.status===`valid`&&p.id===d&&!F&&O?.id===p.id&&O.revision===p.revision?O:null,U=p!==null&&p.validation.status===`valid`&&p.id===d&&!F&&k;async function le(t){let n=await e.request({type:`play.read`}),i=n.presets.find(({id:e})=>e===t)??n.presets.find(({id:e})=>e===d)??n.presets.find(({id:e})=>e===n.currentPresetId)??n.presets[0];c(n),r(n),f(i?.id??``);let a=i===void 0?null:Sa(i);m(a),g(a===null?``:Ca(a.files)),A(null)}async function ue(e){if(!w){T(!0),D(null);try{await e()}catch(e){D({kind:`error`,text:e instanceof Error?e.message:j(`玩法预设操作失败`)})}finally{T(!1)}}}function de(e){if(e===d)return;if(F&&e!==d){D({kind:`error`,text:j(`当前玩法有未保存修改;请先保存或撤销,再切换预设。`)});return}let t=s.presets.find(({id:t})=>t===e);if(t===void 0)return;f(e);let n=Sa(t);m(n),g(Ca(n.files)),ee(null),A(null),D(null),S(`call_chain`)}function fe(){if(M===void 0)return;let e=Sa(M);m(e),g(Ca(e.files)),A(null),D({kind:`status`,text:j(`已撤销当前未保存修改。`)})}function pe(e){p!==null&&h!==``&&me(h,e)}function me(e,t){m(n=>n===null?null:{...n,files:{...n.files,[e]:t}})}function he(e){m(t=>t===null?null:{...t,files:e(structuredClone(t.files))})}function ge(e){m(t=>t?.structure===void 0?t:{...t,structure:e(structuredClone(t.structure))}),A(null)}async function _e(){if(p!==null){if(oe){D({kind:`error`,text:j(`preset.yaml/call-chain.yaml 与结构化编辑同时修改;请保留一种编辑方式后再保存,避免静默覆盖。`)});return}await ue(async()=>{await e.request({type:`play.save`,presetId:p.id,name:p.name,files:p.files,...p.structure===void 0||!R?{}:{structure:p.structure}}),await le(p.id),D({kind:`status`,text:j(`玩法文件与结构化草稿已保存。`)})})}}async function ve(){p!==null&&await ue(async()=>{let t=await e.request({type:`play.export`,presetId:p.id}),n=new Blob([JSON.stringify(t.files,null,2)],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=`${p.name}.play-preset.json`,i.click(),URL.revokeObjectURL(r),D({kind:`status`,text:j(`玩法预设业务文件已导出。`)})})}async function ye(t){t!==void 0&&await ue(async()=>{let n=JSON.parse(await t.text());if(!Array.isArray(n)||n.some(e=>typeof e!=`object`||!e||typeof e.path!=`string`||typeof e.contents!=`string`))throw Error(j(`导入文件必须是 UTF-8 玩法业务文件数组`));await le((await e.request({type:`play.import`,name:t.name.replace(/\.play-preset\.json$/iu,``)||j(`导入玩法`),files:n})).preset.id),D({kind:`status`,text:j(`玩法预设已导入为新的本地身份;请显式启用其中的 JavaScript。`)})})}return(0,K.jsxs)(`section`,{className:`play-preset-screen`,"aria-labelledby":`play-preset-title`,children:[(0,K.jsxs)(`header`,{className:`play-preset-header`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`eyebrow`,children:`PLAY WORKBENCH · FILE NATIVE`}),(0,K.jsx)(`h2`,{id:`play-preset-title`,children:j(`玩法预设`)}),(0,K.jsx)(`p`,{className:`play-preset-lede`,children:j(`在同一处管理设定完善方法、主持规则、调用链、界面产物与可信本地代码。`)})]}),(0,K.jsxs)(`div`,{className:`play-preset-header-fact`,children:[(0,K.jsx)(`span`,{children:j(`新调用链与设定完善当前使用`)}),(0,K.jsx)(`strong`,{children:se?.name??j(`未选择`)}),(0,K.jsx)(`small`,{children:j(`已经开始的模型会话继续使用冻结 revision`)})]})]}),E===null?null:(0,K.jsx)(`div`,{className:`play-preset-feedback ${E.kind}`,role:E.kind===`error`?`alert`:`status`,children:E.text}),(0,K.jsxs)(`fieldset`,{disabled:w,className:`play-preset-workspace`,children:[(0,K.jsx)(`legend`,{className:`visually-hidden`,children:j(`玩法预设工作区`)}),(0,K.jsxs)(`div`,{className:`play-preset-layout`,children:[(0,K.jsxs)(`aside`,{className:`panel-card play-preset-library`,"aria-label":j(`玩法预设列表`),children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`play-preset-section-kicker`,children:`PRESET LIBRARY`}),(0,K.jsx)(`h3`,{children:j(`本地预设`)}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`选择一个本地身份开始编辑。`)})]}),(0,K.jsx)(`ul`,{className:`play-preset-list`,children:s.presets.map(e=>{let t=e.id===d;return(0,K.jsx)(`li`,{children:(0,K.jsxs)(`button`,{className:`play-preset-list-button${t?` selected`:``}`,type:`button`,"aria-pressed":t,disabled:w||F&&!t,onClick:()=>de(e.id),children:[(0,K.jsx)(`span`,{className:`play-preset-list-name`,children:e.name}),(0,K.jsxs)(`span`,{className:`play-preset-list-badges`,children:[e.id===s.currentPresetId?(0,K.jsx)(`span`,{className:`play-preset-badge current`,children:j(`当前玩法`)}):null,(0,K.jsx)(`span`,{className:`play-preset-badge ${e.validation.status}`,children:e.validation.status===`valid`?j(`结构有效`):j(`需要修复`)}),e.enabled===!1?(0,K.jsx)(`span`,{className:`play-preset-badge disabled`,children:j(`已停用`)}):null]})]})},e.id)})}),(0,K.jsxs)(`section`,{className:`play-preset-library-actions`,"aria-label":j(`新建与导入玩法预设`),children:[(0,K.jsx)(`h4`,{children:j(`新建或导入`)}),(0,K.jsx)(`input`,{"aria-label":j(`新玩法预设名称`),placeholder:j(`新玩法预设名称`),value:_,onChange:e=>v(e.currentTarget.value)}),(0,K.jsx)(`button`,{type:`button`,disabled:w||F||_.trim()===``,onClick:()=>void ue(async()=>{let t=await e.request({type:`play.create`,name:_.trim()});v(``),await le(t.preset.id),D({kind:`status`,text:j(`已新建普通玩法预设。`)})}),children:j(`新建空白预设`)}),n.map(t=>(0,K.jsxs)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F,onClick:()=>void ue(async()=>{await le((await e.request({type:`play.create`,name:t.name,files:structuredClone(t.files)})).preset.id),D({kind:`status`,text:j(`已复制推荐{name};所有文件均可编辑。`,{name:t.label})})}),children:[j(`复制推荐`),t.label]},t.id)),(0,K.jsxs)(`label`,{className:`play-preset-import-control`,children:[j(`导入玩法文件`),(0,K.jsx)(`input`,{"aria-label":j(`导入玩法预设文件`),type:`file`,accept:`.json,application/json`,disabled:w||F,onChange:e=>{ye(e.currentTarget.files?.[0]),e.currentTarget.value=``}})]})]})]}),p===null?(0,K.jsx)(`section`,{className:`panel-card`,role:`status`,children:j(`还没有玩法预设。`)}):(0,K.jsxs)(`section`,{className:`panel-card play-preset-editor`,"aria-label":j(`玩法预设文件编辑器`),children:[(0,K.jsxs)(`header`,{className:`play-preset-editor-header`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`play-preset-section-kicker`,children:`EDIT PRESET`}),(0,K.jsx)(`h3`,{children:p.name}),(0,K.jsxs)(`p`,{className:`field-note`,children:[`revision `,p.revision,` `,j(`· 修改只影响之后开始的全新上下文`)]})]}),(0,K.jsxs)(`div`,{className:`play-preset-editor-badges`,children:[(0,K.jsx)(`span`,{className:`play-preset-draft-state${F?` dirty`:``}`,children:j(F?`未保存修改`:`已保存`)}),p.id===s.currentPresetId?(0,K.jsx)(`span`,{className:`play-preset-badge current`,children:j(`当前玩法`)}):null,p.enabled===!1?(0,K.jsx)(`span`,{className:`play-preset-badge disabled`,children:j(`已停用`)}):null,(0,K.jsx)(`span`,{className:`play-preset-badge`,children:p.scriptsEnabled===!0?j(`JavaScript 已启用`):j(`JavaScript 已停用`)})]})]}),(0,K.jsxs)(`label`,{className:`play-preset-name-field`,children:[j(`预设名称`),(0,K.jsx)(`input`,{"aria-label":j(`玩法预设名称`),maxLength:160,value:p.name,onChange:e=>m({...p,name:e.currentTarget.value})})]}),(0,K.jsxs)(`details`,{className:`play-preset-operations`,children:[(0,K.jsx)(`summary`,{children:j(`预设操作`)}),(0,K.jsxs)(`div`,{className:`play-preset-management`,"aria-label":j(`玩法预设身份管理`),children:[(0,K.jsx)(`p`,{children:j(`这些操作只管理这份本地预设;内容编辑和保存仍在页面底部完成。`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F,onClick:()=>void ue(async()=>{await e.request({type:`play.enable`,presetId:p.id,enabled:p.enabled===!1}),await le(p.id),D({kind:`status`,text:j(`玩法预设状态已更新。`)})}),children:p.enabled===!1?j(`启用预设`):j(`停用预设`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F,onClick:()=>void ue(async()=>{await e.request({type:`play.scripts`,presetId:p.id,enabled:p.scriptsEnabled!==!0}),await le(p.id),D({kind:`status`,text:p.scriptsEnabled===!0?j(`JavaScript 已停用;raw/document 仍可预览。`):j(`JavaScript 已显式启用(本地可信代码)。`)})}),children:p.scriptsEnabled===!0?j(`停用 JavaScript`):j(`启用 JavaScript(本地可信代码)`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F,onClick:()=>void ue(async()=>{await le((await e.request({type:`play.copy`,presetId:p.id})).preset.id),D({kind:`status`,text:j(`已复制为独立本地身份。`)})}),children:j(`复制为新预设`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F,onClick:()=>void ve(),children:j(`导出业务文件`)}),(0,K.jsx)(`button`,{type:`button`,className:`danger-button`,disabled:w||F,onClick:()=>void ue(async()=>{await e.request({type:`play.delete`,presetId:p.id}),await le(),D({kind:`status`,text:j(`玩法预设已删除;删空后会自动重建默认预设。`)})}),children:j(`删除预设`)})]}),(0,K.jsx)(`p`,{className:`field-note`,children:j(`导入的 JavaScript 默认停用;启用表示你信任这些本地文件,而不是获得安全沙箱保证。`)})]}),(0,K.jsxs)(`div`,{className:`play-preset-overview`,"aria-label":j(`玩法预设摘要`),children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{children:j(`后置请求`)}),(0,K.jsx)(`strong`,{children:z})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{children:j(`产物输出`)}),(0,K.jsx)(`strong`,{children:B})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{children:j(`界面挂载`)}),(0,K.jsx)(`strong`,{children:ce})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{children:j(`普通文件`)}),(0,K.jsx)(`strong`,{children:V})]})]}),(0,K.jsx)(`nav`,{className:`play-preset-workspace-nav`,role:`tablist`,"aria-label":j(`玩法预设编辑区域`),children:xa.map(e=>(0,K.jsxs)(`button`,{id:`play-preset-tab-${e.id}`,type:`button`,role:`tab`,"aria-controls":`play-preset-panel-${e.id}`,"aria-selected":x===e.id,className:x===e.id?`selected`:``,onClick:()=>S(e.id),children:[(0,K.jsx)(`strong`,{children:j(e.label)}),(0,K.jsx)(`span`,{children:j(e.description)})]},e.id))}),p.structure===void 0||x!==`call_chain`&&x!==`extensions`?null:(0,K.jsx)(Ea,{view:x,structure:p.structure,files:p.files,workbench:H,workbenchPending:U,structuredError:ne,onChange:ge,onFileChange:me,onCreateFile:(e,t)=>he(n=>({...n,[e]:t})),onError:A}),p.structure!==void 0&&x===`setting_improvement`?(0,K.jsx)(Ta,{structure:p.structure,files:p.files,systemPrompt:o,onChange:ge,onFileChange:me,onCreateFile:(e,t)=>he(n=>({...n,[e]:t}))}):null,x===`blocks`&&(0,K.jsx)(Za,{files:p.files,pending:w,onChange:e=>he(()=>structuredClone(e)),onFeedback:D}),x===`files`?(0,K.jsx)(Ua,{files:p.files,filePath:h,newFilePath:y,pending:w,onFilePathChange:g,onNewFilePathChange:b,onFileChange:pe,onCreateFile:()=>{let e=y.trim();if(p.files[e]!==void 0){D({kind:`error`,text:j(`该玩法文件路径已经存在。`)});return}he(t=>({...t,[e]:``})),g(e),b(``),D({kind:`status`,text:j(`已加入普通文件草稿;保存时会通过 codec 校验。`)})}}):null,p.structure===void 0&&(x===`call_chain`||x===`extensions`||x===`setting_improvement`)?(0,K.jsxs)(`section`,{id:`play-preset-panel-${x}`,className:`play-preset-empty-view`,role:`tabpanel`,"aria-labelledby":`play-preset-tab-${x}`,children:[(0,K.jsx)(`strong`,{children:j(`结构化编辑暂不可用`)}),(0,K.jsx)(`p`,{children:j(`当前草稿无法生成结构投影。请到“高级文件”修复 preset.yaml 或 call-chain.yaml,保存后再回来。`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,onClick:()=>S(`files`),children:j(`前往高级文件`)})]}):null,x===`preview`?(0,K.jsxs)(`section`,{id:`play-preset-panel-preview`,className:`play-preset-preview-workspace`,role:`tabpanel`,"aria-labelledby":`play-preset-tab-preview`,children:[(0,K.jsxs)(`header`,{className:`play-preset-workspace-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`play-preset-section-kicker`,children:`FROZEN REVISION`}),(0,K.jsx)(`h4`,{children:j(`预览当前预设`)})]}),(0,K.jsx)(`p`,{children:j(`产物外观与真实调用链提示词都留在这里检查,不会调用模型或离开当前预设。`)})]}),U?(0,K.jsx)(`p`,{role:`status`,children:j(`正在生成真实编译/产物预览…`)}):null,H?.staticErrors.length?(0,K.jsx)(`ul`,{"aria-label":j(`工作台静态错误`),children:H.staticErrors.map(e=>(0,K.jsxs)(`li`,{children:[e.location,` · `,e.message]},`${e.location}:${e.code}`))}):null,(0,K.jsx)(Ka,{workbench:H}),(0,K.jsxs)(`section`,{className:`play-preset-prompt-preview`,"aria-label":j(`当前预设的真实提示词预览`),children:[(0,K.jsx)(`h3`,{children:j(`真实调用链预览`)}),F?(0,K.jsx)(`p`,{className:`field-note`,children:j(`请先保存当前修改;真实预览只编译已冻结的有效 revision。`)}):p.validation.status===`valid`?a===void 0?(0,K.jsx)(`p`,{className:`field-note`,children:j(`当前宿主没有提供提示词预览面板。`)}):a({presetId:p.id,revision:p.revision}):(0,K.jsx)(`p`,{className:`field-note`,children:j(`当前 revision 需要修复,暂时不能编译真实调用链。`)})]})]}):null,(0,K.jsxs)(`footer`,{className:`play-preset-editor-actions`,children:[oe?(0,K.jsx)(`p`,{role:`alert`,className:`workspace-feedback`,children:j(`preset.yaml/call-chain.yaml 与结构化字段均有未保存修改;请撤销其中一侧后再保存,避免 stale structure 覆盖 raw YAML。`)}):null,(0,K.jsxs)(`div`,{className:`play-preset-save-state`,children:[(0,K.jsx)(`strong`,{children:F?j(`草稿尚未保存`):p.validation.status===`valid`?j(`结构校验通过`):j(`草稿需要修复`)}),(0,K.jsx)(`span`,{children:p.validation.status===`valid`?`revision ${p.revision}`:`[${p.validation.code??`play_preset_invalid`}] ${p.validation.location??`call-chain.yaml`}:${p.validation.message}`})]}),(0,K.jsxs)(`div`,{className:`play-preset-primary-actions`,children:[F?(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w,onClick:fe,children:j(`撤销未保存修改`)}):null,(0,K.jsx)(`button`,{type:`button`,disabled:w||!F||oe,onClick:()=>void _e(),children:j(`保存修改`)}),(0,K.jsx)(`button`,{type:`button`,className:`secondary-button`,disabled:w||F||p.validation.status!==`valid`,onClick:()=>void ue(async()=>{await e.request({type:`play.select`,presetId:p.id}),await le(p.id),D({kind:`status`,text:j(`已将该冻结 revision 设为当前玩法。`)})}),children:j(`应用为当前玩法`)})]})]})]})]})]})]})}function Ta({structure:e,files:t,systemPrompt:n,onChange:r,onFileChange:i,onCreateFile:a}){let o=e.settingImprovementPrompt;return(0,K.jsxs)(`section`,{id:`play-preset-panel-setting_improvement`,className:`play-preset-structured-editor`,role:`tabpanel`,"aria-labelledby":`play-preset-tab-setting_improvement`,children:[(0,K.jsxs)(`header`,{className:`play-preset-workspace-heading`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`play-preset-section-kicker`,children:`AI AUTHORING`}),(0,K.jsx)(`h3`,{children:j(`AI 设定完善`)})]}),(0,K.jsx)(`p`,{children:j(`这份文字决定 AI 怎样理解、规划和创作内容包设定;每次开始完善时会冻结当前预设 revision。`)})]}),(0,K.jsxs)(`div`,{className:`play-preset-concept-note`,children:[(0,K.jsx)(`strong`,{children:j(`工具为什么不在这里?`)}),(0,K.jsx)(`p`,{children:j(`Runtime 继续内置 setting_* 工具定义、参数、说明、只读/写入阶段边界和候选终态协议。预设只能编辑创作语义,不能替换这些机械契约。`)})]}),o===void 0?(0,K.jsxs)(`div`,{className:`play-preset-structured-section`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h4`,{children:j(`沿用系统推荐提示`)}),(0,K.jsx)(`p`,{children:j(`这是一份功能加入前保存的 v1 预设。打开页面不会改写它;写入后才会产生新的预设 revision。`)})]}),(0,K.jsx)(`textarea`,{"aria-label":j(`系统推荐设定完善提示词`),value:n,readOnly:!0,spellCheck:!1}),(0,K.jsx)(`button`,{type:`button`,onClick:()=>{a(b,t[`prompts/setting-improvement.md`]?.trim()?t[b]:n),r(e=>({...e,settingImprovementPrompt:{role:`author_instruction`,path:b}}))},children:j(`写入预设并编辑`)})]}):(0,K.jsx)(`div`,{className:`play-preset-structured-section`,children:(0,K.jsx)(ka,{label:j(`设定完善创作提示`),path:o.path,paths:La(t),files:t,onPathChange:e=>r(t=>({...t,settingImprovementPrompt:{...o,path:e}})),onContentsChange:e=>i(o.path,e)})})]})}function Ea({view:e,structure:t,files:n,workbench:r,workbenchPending:i,structuredError:a,onChange:o,onFileChange:s,onCreateFile:c,onError:l}){let u=La(n),d=t.followups.flatMap((e,t)=>e.artifacts.map((n,r)=>({followup:e,followupIndex:t,artifact:n,artifactIndex:r}))),f=new Set(d.map(({artifact:e})=>e.channel)),p=t.mounts.filter(({channel:e})=>!f.has(e));function m(){let e=za(n,`prompts/narrative-${t.narrativePrompts.length+1}.md`);c(e,j(`# 叙事规则
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="color-scheme" content="dark" />
7
7
  <title>Narraeon</title>
8
- <script type="module" crossorigin src="/assets/index-D5VqDvjY.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D8YKfmNG.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-Xa0s35CJ.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "narraeon",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "A local-first workspace for living worlds.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",