@logictan/dsh-imagegen 2.0.2 → 2.0.4

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.
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["css","css"],"sources":["../src/client/locales.ts","../src/locale-tables.ts","../src/client/helpers.ts","../src/client/settings-form.ts","../src/client/channels-form.ts","../src/model-catalog.ts","../src/protocol.ts","../src/client/use-language.ts","../src/client/SettingsCard.tsx","../src/client/settings-scope.ts","../src/client/image-toolview.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * dsh-imagegen surface copy: zh is the key source, en mirrors every key.\n */\n\nexport const zh = {\n 'entry.label': 'AI 生图',\n 'entry.tooltip': 'AI 生图面板(gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream 系列)',\n 'entry.newSession': '新会话',\n 'entry.newSessionTooltip': '创建一个新的 DSH 会话',\n 'entry.image': '生图',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': '觉得好用或有建议?欢迎来 GitHub 提 issues、点个 star 支持一下!',\n 'conversation.add': '加入对话',\n 'conversation.adding': '加入中…',\n 'conversation.added': '图片已加入当前对话',\n 'conversation.addHint': '加入对话后可使用 /edit_image 修改;命令使用插件图片模型',\n 'conversation.noSession': '请先打开一个会话,再加入图片',\n 'conversation.unavailable': '当前会话暂不可用',\n 'conversation.busy': '当前对话正在发送,请稍后再试',\n // mode\n 'mode.text': '文生图',\n 'mode.edit': '图生图',\n 'workspace.label': '创作模式',\n 'workspace.normal': '普通生图',\n 'workspace.canvas': '无限画布',\n 'workspace.ecommerce': '电商模式',\n 'canvas.addImage': '添加图片',\n 'canvas.addText': '添加文本',\n 'canvas.generate': '生成图片',\n 'canvas.addToCanvas': '加入画布',\n 'canvas.untitled': '未命名画布',\n 'canvas.imageNode': '图片节点',\n 'canvas.textNode': '文本节点',\n 'canvas.fromGallery': '素材库图片',\n 'canvas.fromHistory': '历史图片',\n 'canvas.project': '画布项目',\n 'canvas.newCanvas': '新建画布',\n 'canvas.deleteCanvas': '删除画布',\n 'canvas.deleteCanvasConfirm': '再点一次确认删除',\n 'canvas.rename': '重命名画布',\n 'canvas.renameHint': '双击重命名',\n 'canvas.background': '画布背景',\n 'canvas.backgroundDots': '点阵',\n 'canvas.backgroundLines': '网格线',\n 'canvas.backgroundWaves': '波浪线',\n 'canvas.backgroundShapeGrid': '滚动方格',\n 'canvas.backgroundDotField': '点场',\n 'canvas.backgroundDotGrid': '互动点阵',\n 'canvas.backgroundFloatingLines': '浮动线条',\n 'canvas.backgroundFlow': '流体扰动',\n 'canvas.backgroundLiquid': '液态以太',\n 'canvas.backgroundFaultyTerminal': '故障终端',\n 'canvas.backgroundSilk': '丝绸',\n 'canvas.backgroundGalaxy': '星系',\n 'canvas.backgroundBlank': '空白',\n 'canvas.backgroundUpload': '上传自定义背景…',\n 'canvas.backgroundRemove': '移除自定义背景',\n 'canvas.templateLibrary': '模板库(选择提示词生成文本+配置节点)',\n 'canvas.undo': '撤销 (Ctrl+Z)',\n 'canvas.redo': '重做 (Ctrl+Shift+Z)',\n 'canvas.saving': '保存中…',\n 'canvas.saved': '已保存',\n 'canvas.saveFailed': '保存失败',\n 'canvas.loading': '加载中…',\n 'canvas.toolSelect': '选择 / 移动节点',\n 'canvas.toolPan': '拖动画布(空格 / Ctrl + 拖动)',\n 'canvas.delete': '删除',\n 'canvas.duplicate': '创建副本 (Ctrl+D)',\n 'canvas.download': '下载图片',\n 'canvas.fitView': '适应全部内容',\n 'canvas.minimapOpen': '打开缩略图',\n 'canvas.minimapClose': '关闭缩略图',\n 'canvas.minimap': '画布缩略图',\n 'canvas.zoom': '缩放',\n 'canvas.textPlaceholder': '输入文本内容…',\n 'canvas.generatingNode': '生成中…',\n 'canvas.generateFailed': '生成失败',\n 'canvas.retry': '重试',\n 'canvas.taskLost': '生成任务已丢失(服务重启),请重试',\n 'canvas.emptyImageNode': '点击上传图片',\n 'canvas.connectHint': '拖到其他节点建立连线',\n 'canvas.connectAddHint': '拖到其他节点建立连线;点击直接添加下一节点',\n 'canvas.resizeHint': '拖动调整大小(图片锁定比例)',\n 'canvas.count': '生成数量',\n 'canvas.configNode': '生成配置',\n 'canvas.addConfigNode': '添加配置节点',\n 'canvas.configHint': '把图片节点连线到左侧作为参考图,文本节点连线过来作为提示词;选中本节点后在下方输入或改写提示词并生成。',\n 'canvas.composerLinked': '已连接 {count} 个输入节点',\n 'canvas.composerPlaceholder': '描述要生成的图片;也可把文本节点连线过来作为提示词…',\n 'canvas.countUnit': '{count} 张',\n 'canvas.model': '生图模型',\n 'canvas.modelPlaceholder': '选择模型',\n 'canvas.size': '尺寸比例',\n 'canvas.sizeAuto': '自动比例',\n 'canvas.quality': '清晰度',\n 'canvas.qualityAuto': '自动清晰度',\n 'canvas.needApi': '请先配置生图 API',\n 'canvas.needPrompt': '请输入生成描述',\n 'canvas.needModel': '尚未配置可用生图模型',\n 'canvas.openSettings': '打开设置',\n 'canvas.deleteConnection': '删除连线',\n 'canvas.paste': '粘贴 (Ctrl+V)',\n 'canvas.addImageNode': '添加图片节点',\n 'canvas.addTextNode': '添加文本节点',\n 'canvas.addSketch': '添加画板(手绘草图生图)',\n 'canvas.sketchNode': '画板节点',\n 'canvas.addSketchNode': '添加画板节点',\n 'canvas.sketchPlaceholder': '在此自由绘制草图,连线到配置节点即可作为参考图生成',\n 'canvas.sketchBrush': '画笔',\n 'canvas.sketchEraser': '橡皮擦',\n 'canvas.sketchUndo': '撤销上一笔',\n 'canvas.sketchClear': '清空画板',\n 'canvas.sketchWidth': '笔触粗细',\n 'canvas.dismiss': '关闭提示',\n 'canvas.annotate': '标注:在图片上画框,自动挂上提示词卡片',\n 'canvas.annotationNode': '标注提示词',\n 'canvas.annotationTag': '标注',\n 'canvas.annotationPlaceholder': '描述这个框里要改成什么,例如:把这里换成一只橘猫',\n 'canvas.annotationDrawHint': '在图片上拖拽画框;松开后从框上拉出引导线并挂一张提示词卡片(跟随图片移动,无需连线)',\n 'canvas.removeBackground': '移除背景(本地抠图,输出透明 PNG)',\n 'canvas.removingBackground': '正在移除背景…',\n 'canvas.removeBgDone': '已移除背景(透明像素 {percent}%)',\n 'canvas.removeBgFailed': '背景太复杂,本地抠图没有找到可移除的背景;可以先用「标注」框选主体再让模型重绘',\n 'canvas.alreadyTransparent': '这张图片已经是透明背景了',\n 'canvas.noBackgroundNode': '去背景图片',\n 'canvas.splitLayers': '图层拆分(AI 拆成可编辑图层)',\n 'canvas.splittingLayers': '正在拆分图层…',\n 'canvas.layerSplitDone': '已拆分为 {count} 个图层节点',\n 'canvas.layerSplitEmpty': '模型没有识别出可拆分的图层',\n 'canvas.layerBackground': '背景层 · {label}',\n 'canvas.layerObject': '图层 · {label}',\n 'canvas.layerText': '文字层 · {label}',\n 'canvas.layerKindBackground': '背景',\n 'canvas.layerKindObject': '元素',\n 'canvas.layerKindText': '文字',\n 'canvas.nodeModel': '本节点使用的模型(重试与相连配置节点默认使用)',\n 'canvas.modelDefault': '默认模型',\n 'canvas.fontSmaller': '缩小字号',\n 'canvas.fontLarger': '放大字号',\n 'canvas.bold': '加粗',\n 'canvas.textColor': '文字颜色',\n 'canvas.colorDefault': '跟随主题',\n 'canvas.close': '关闭',\n 'canvas.tabUpload': '上传',\n 'canvas.tabHistory': '历史记录',\n 'canvas.tabGallery': '素材库',\n 'canvas.imageMenuUpload': '本地图片',\n 'canvas.imageMenuAssets': '素材库',\n 'canvas.imageMenuHistory': '历史记录',\n 'canvas.imageMenuGenerate': 'AI生成',\n 'canvas.tabGenerate': 'AI 生成',\n 'canvas.dropHint': '图片拖动到此处即可添加',\n 'canvas.dropSub': '支持多选,PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': '未命名作品',\n 'canvas.picked': '已选 {count} 张',\n 'canvas.generateAndAdd': '生成并添加到画布',\n 'chat.expand': '展开右侧对话',\n 'chat.collapse': '收起右侧对话',\n 'chat.toggle': '对话',\n 'config.resizeHint': '拖拽调整参数栏宽度',\n 'ecommerce.generation': '生成设置',\n 'ecommerce.title': '电商模式',\n 'ecommerce.badge': '预览',\n 'ecommerce.short': '电商',\n 'ecommerce.anchorPending': '已生成主图,其余图片正在自动参考主图生成,保证整套同款…',\n 'ecommerce.anchorFailed': '主图生成失败,其余图片已暂停。请重试主图(或整组重新生成)后再继续。',\n 'ecommerce.anchorNote': '锚定生成:先出主图,其余图片自动参考主图,保证整套同款',\n 'ecommerce.noAssetWarn': '未上传商品图:主图将按文字描述生成并作为锚定基准,建议先上传商品主图获得更高一致性',\n 'ecommerce.product': '商品信息',\n 'ecommerce.productName': '商品名称(必填)',\n 'ecommerce.projectName': '项目名称(可选)',\n 'ecommerce.constraints': '卖点与约束',\n 'ecommerce.sellingPoints': '参考信息:品牌型号、规格(容量/尺寸/克重)、材质工艺、核心卖点、安全认证、配件清单等',\n 'ecommerce.protectedFeatures': '必须保留的特征,例如:颜色、Logo、包装文字、结构',\n 'ecommerce.styleHint': '风格补充,例如:高级感 / 写实 / 自然光',\n 'ecommerce.setStructure': '套图结构',\n 'ecommerce.preview': '生成套图预览',\n 'ecommerce.planTitle': '预计生成 {count} 张图片',\n 'ecommerce.planSlot': '{count} 张 · {description}',\n 'ecommerce.confirm': '确认生成整套图片',\n 'ecommerce.uploadRef': '上传商品图(必选,最多 4 张:主体 / 包装 / 细节)',\n 'ecommerce.uploadShort': '上传素材',\n 'ecommerce.sellingTitle': '商品卖点',\n 'ecommerce.advanced': '高级选项(平台 / 约束)',\n 'ecommerce.params': '参数选择',\n 'ecommerce.platformLabel': '平台',\n 'ecommerce.languageLabel': '文案语言',\n 'ecommerce.customLanguageOption': '自定义…',\n 'ecommerce.customLanguageLabel': '自定义语言',\n 'ecommerce.customLanguagePlaceholder': '输入语言名称,例如:Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': '比例',\n 'ecommerce.categoryLabel': '类目',\n 'ecommerce.multiSelect': '可多选',\n 'ecommerce.countHint': '点击调整数量(1-4)',\n 'ecommerce.refSettings': '参考图设置(可选)',\n 'ecommerce.styleTitle': '风格 / 补充提示',\n 'ecommerce.protectedLabel': '必须保留的特征(可选)',\n 'ecommerce.assetsFull': '最多上传 4 张商品图',\n 'ecommerce.productRefTitle': '主图参考图',\n 'ecommerce.productRefHint': '必选 · 商品主体图,最多 4 张',\n 'ecommerce.styleRefTitle': '风格参考图',\n 'ecommerce.styleRefBadge': '可选,最多 1 张',\n 'ecommerce.styleRefHint': '只传风格、光影、构图、氛围,不能更换商品主体。',\n 'ecommerce.styleRefUpload': '上传风格参考图',\n 'ecommerce.refInfoTitle': '参数信息',\n 'ecommerce.aiWrite': 'AI 帮写',\n 'ecommerce.aiWriting': 'AI 整理中…',\n 'ecommerce.aiWriteHint': '根据商品名称与类目自动整理参数信息(提示词)',\n 'ecommerce.promptInfoPlaceholder': '选填:品牌型号、规格(容量/尺寸/克重)、材质工艺、核心卖点、安全认证、配件清单、画面要求等,也可以点右上角 AI 帮写',\n 'ecommerce.optional': '可选',\n 'ecommerce.previewBoardTitle': '生成前预览',\n 'ecommerce.previewBoardHint': '每张图的提示词都可以在生成前修改',\n 'ecommerce.promptReset': '恢复默认',\n 'ecommerce.promptAutoHint': '自动生成 · 可直接修改',\n 'ecommerce.refSelect': '该用途使用的参考图',\n 'ecommerce.refNone': '不使用参考图',\n 'ecommerce.role.product': '商品主体',\n 'ecommerce.role.packaging': '包装',\n 'ecommerce.role.detail': '细节/角度',\n 'ecommerce.role.style': '风格参考',\n 'ecommerce.footerReady': '将生成 {count} 张套图',\n 'ecommerce.footerEmpty': '请选择套图结构',\n 'ecommerce.results.title': '套图结果',\n 'ecommerce.results.progress': '{done}/{total} 已完成',\n 'ecommerce.results.failed': '{count} 张失败',\n 'ecommerce.results.empty': '确认生成后,每张图片的任务状态和结果会按用途显示在这里',\n 'ecommerce.results.regenerate': '重新生成',\n 'ecommerce.results.export': '导出清单',\n 'ecommerce.results.newProduct': '新商品',\n // prompt\n 'prompt.placeholder': '描述你想要的画面,例如:一只戴着宇航员头盔的橘猫,在月球上举起望远镜,水彩风格,柔和光线…',\n 'prompt.required': '请输入提示词',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': '超出该模型 {limit} 字符的提示词上限,请精简后再生成',\n 'prompt.enhance': '增强',\n 'prompt.enhancing': '增强中…',\n 'prompt.enhanceHint': '使用已配置的对话模型扩写提示词',\n 'prompt.configTitle': '请先配置提示词增强模型',\n 'prompt.configHint': '已打开设置。进入「插件 → AI 生图」,填写或复用 API 地址和密钥,选择对话模型并保存。',\n 'tasks.title': '生成任务',\n 'tasks.queued': '排队中',\n 'tasks.running': '生成中',\n 'tasks.completed': '已完成',\n 'tasks.failed': '失败',\n 'tasks.cancelled': '已取消',\n 'tasks.cancel': '取消',\n 'tasks.retry': '重试',\n // parameters\n 'params.size': '尺寸',\n 'params.quality': '清晰度',\n 'params.count': '生成数量',\n 'params.detail': '细节',\n 'size.auto': '自动',\n 'size.square': '1:1 方图',\n 'size.portrait34': '3:4 标准竖图',\n 'size.landscape43': '4:3 标准横图',\n 'size.portrait916': '9:16 竖屏',\n 'size.portrait23': '2:3 竖图',\n 'size.landscape32': '3:2 横图',\n 'size.wide169': '16:9 宽屏',\n 'size.ultrawide21': '21:9 超宽屏',\n 'quality.auto': '自动',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1 张',\n 'count.two': '2 张',\n 'count.three': '3 张',\n 'count.four': '4 张',\n 'detail.auto': '自动',\n 'detail.standard': '标准',\n 'detail.high': '高清',\n 'detail.hint': '透传参数,部分 gpt-image-2 网关支持;官方接口请保持「自动」',\n // footer\n 'model.label': '模型',\n 'templates.favoritesCount': '共 {count} 个收藏',\n 'model.channel': '渠道',\n 'model.channelPlaceholder': '默认渠道',\n 'model.noEditModels': '当前模式暂无可用模型',\n 'compare.enable': '多模型对比',\n 'compare.models': '参与对比的模型',\n 'compare.title': '多模型结果对比',\n 'compare.fullscreen': '全屏对比',\n 'compare.selectRequired': '请至少选择一个对比模型',\n 'generate': '开始生成',\n 'generating': '生成中…',\n // edit reference image\n 'edit.upload': '点击或拖拽上传参考图片',\n 'edit.uploadHint': 'PNG / JPG / WEBP,不超过 10MB',\n 'edit.change': '更换图片',\n 'edit.remove': '移除',\n 'edit.required': '请先上传参考图片',\n // canvas\n 'canvas.emptyTitle': '开始你的创作',\n 'canvas.emptyHint': '双击空白处或用底部工具添加图片/文本节点;把图片和文本节点连线到目标节点,选中后即可在底部生成器中出图',\n 'canvas.new': '新建作图',\n 'canvas.newHint': '开始新的作图并清空预览',\n 'canvas.error': '生成失败:{error}',\n 'canvas.submitting': '正在加入生成队列…',\n 'canvas.queued': '已加入生成队列',\n 'canvas.queueHint': '当前有 {count} 个任务等待处理',\n 'canvas.generating': '正在生成图片…',\n 'canvas.elapsed': '已用时 {seconds}s',\n 'canvas.images': '本次生成 {count} 张',\n 'download': '下载',\n 'revisedPrompt': '优化提示词:{prompt}',\n // history\n 'history.title': '历史记录',\n 'history.empty': '暂无历史记录,生成图片后会保存在这里',\n 'history.clear': '清空',\n 'history.clearConfirm': '确定要清空全部历史记录吗?此操作不可撤销。',\n 'history.restore': '恢复',\n 'history.delete': '删除',\n 'history.images': '张',\n 'history.viewing': '历史 · {time}',\n 'history.search': '搜索提示词或模型…',\n 'history.model': '模型筛选',\n 'history.ratio': '比例筛选',\n 'history.allModels': '全部模型',\n 'history.allRatios': '全部比例',\n // gallery\n 'gallery.title': '素材库',\n 'gallery.categories': '分类',\n 'gallery.all': '全部素材',\n 'gallery.upload': '上传素材',\n 'gallery.uploadModel': '本地上传',\n 'gallery.uploaded': '素材已入库',\n 'gallery.uploadHint': '把本地图保存进素材库,画布随时可用',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': '画面比例',\n 'gallery.tags': '标签',\n 'gallery.filterHint': '按生成模式、模型和比例筛选素材',\n 'gallery.count': '· 共 {count} 幅',\n 'gallery.viewMode': '视图模式',\n 'gallery.masonry': '瀑布流',\n 'gallery.grid': '整齐网格',\n 'gallery.sort': '排序',\n 'gallery.newest': '最新发布',\n 'gallery.oldest': '最早发布',\n 'gallery.untitled': '未命名作品',\n 'gallery.search': '搜索作品或模型…',\n 'gallery.tagsPlaceholder': '标签,用逗号分隔',\n 'gallery.tagsApply': '添加标签',\n 'gallery.editTags': '编辑标签',\n 'gallery.tagsEditShort': '编辑',\n 'gallery.tagsSave': '保存',\n 'gallery.tagsCancel': '取消',\n 'gallery.selected': '已选 {count} 项',\n 'gallery.selectionDone': '完成选择',\n 'gallery.selectionClear': '取消选择',\n 'gallery.downloadSelected': '下载所选',\n 'gallery.exportJson': '导出 JSON',\n 'gallery.select': '选择作品',\n 'gallery.add': '加入素材库',\n 'gallery.added': '已加入素材库',\n 'gallery.already': '已在素材库中',\n 'gallery.delete': '移出素材库',\n 'gallery.openFolder': '打开文件夹',\n 'gallery.clear': '清空素材库',\n 'gallery.openFolderHint': '在系统的文件管理器中打开图片保存目录(~/.dsh/dsh-imagegen)',\n 'gallery.clearConfirm': '确定要清空整个素材库吗?此操作不可撤销。',\n 'gallery.empty': '素材库还是空的,把喜欢的图片加入进来吧',\n 'gallery.viewing': '素材库 · {time}',\n // preview\n 'preview.title': '图片预览',\n 'preview.open': '点击预览',\n 'preview.close': '关闭',\n 'preview.prev': '上一张',\n 'preview.next': '下一张',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': '图片缩放控制',\n 'preview.zoomIn': '放大',\n 'preview.zoomOut': '缩小',\n 'preview.zoomReset': '重置缩放',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': '复制提示词',\n 'preview.copied': '已复制',\n 'preview.addToEdit': '添加到图生图',\n // config banner\n 'config.missing': '尚未配置 API:请前往「设置 → 插件 → 可配置」为 AI 生图填写 api_url 与 api_key。',\n 'config.generationTitle': '请先配置生图 API',\n 'config.generationHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写生图 API 地址与 API 密钥,保存后即可开始生成。',\n 'config.enhancementTitle': '请先配置提示词增强模型',\n 'config.enhancementHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写或复用对话 API 地址和密钥,选择对话模型并保存。',\n 'config.disabledTitle': 'AI 生图插件已停用',\n 'config.disabledHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。开启「启用插件」后即可生成图片。',\n 'config.configured': '已连接 {url}',\n 'config.disabled': '插件已停用,请在设置中重新启用。',\n 'connection.connected': '已连接',\n 'connection.disconnected': '未连接',\n 'panel.collapseConfig': '收起参数栏',\n 'panel.expandConfig': '展开参数栏',\n // plugin update\n 'update.available': '检测到新版本:{version}',\n 'update.install': '在线更新',\n 'update.installing': '更新中…',\n 'update.success': '已更新到 {version},请重启 DSH',\n 'update.failed': '更新失败,请重试',\n 'update.release': '查看 Release',\n // settings card\n 'settings.title': 'AI 生图(dsh-imagegen)',\n 'settings.description': '配置图像生成 API 地址与密钥',\n 'settings.summary': '配置图像生成渠道、模型与插件开关',\n 'settings.currentVersion': '当前版本',\n 'settings.apiUrl': 'API 地址(api_url)',\n 'settings.apiUrlHint': 'OpenAI 兼容接口基址,如 https://api.openai.com/v1;将自动拼接 /images/generations 与 /images/edits',\n 'settings.apiKey': 'API 密钥(api_key)',\n 'settings.apiKeyHint': 'Bearer 密钥,明文存于本机设置文档;界面只显示是否已设置',\n 'settings.apiKeySet': '已保存密钥;输入新值可更换,点击「清除」可删除',\n 'settings.apiKeyClear': '清除',\n 'settings.imageModelsTitle': '生图模型',\n 'settings.imageModelsHint': '保存 API 地址和密钥后检测候选模型;请只选择实际支持生图的项。',\n 'settings.imageModels': '允许使用的生图模型',\n 'settings.imageModelsManualHint': '一行一个模型;API 未提供 /models 时可手动填写。面板和 Agent 只能使用此列表。',\n 'settings.imageModelsFetch': '检测可用模型',\n 'settings.imageModelsLoading': '检测中…',\n 'settings.imageModelsCandidates': '检测到的候选模型(勾选后保存)',\n 'settings.addModel': '+ 手动添加',\n 'settings.cancelAddModel': '收起添加',\n 'settings.addModelPlaceholder': '输入模型名称,例如 qwen-image',\n 'settings.addModelConfirm': '添加',\n 'settings.removeModel': '移除模型',\n 'settings.optional': '可选',\n 'settings.moreOptions': '更多设置',\n 'settings.promptEnhanceTitle': '提示词增强模型',\n 'settings.promptEnhanceHint': '用于将简短描述扩写为更完整的生图提示词;留空的地址和密钥会复用生图配置。',\n 'settings.promptApiUrl': '对话 API 地址(可选)',\n 'settings.promptApiUrlHint': 'OpenAI 兼容基址;留空则复用图像 API 地址。',\n 'settings.promptApiKey': '对话 API 密钥(可选)',\n 'settings.promptApiKeyHint': '留空则复用图像 API 密钥。',\n 'settings.promptModel': '对话模型',\n 'settings.promptModelHint': '选择或填写支持 /chat/completions 的模型。',\n 'settings.promptModelDetectionHint': '默认复用生图 API;检测后点选一个模型即可。',\n 'settings.promptModelsFetch': '获取可用模型',\n 'settings.promptModelsLoading': '正在获取…',\n 'settings.promptModelsSelect': '选择一个模型',\n 'settings.promptModelsCandidates': '检测到的候选对话模型',\n 'settings.addPromptModelPlaceholder': '输入对话模型名称,例如 gpt-4.1-mini',\n 'settings.promptApiAdvanced': '使用独立对话 API(可选)',\n 'settings.announceToAgent': '向 Agent 播报本插件',\n 'settings.announceToAgentHint': '开启后,本插件的存在与能力会写入每个 Agent 的系统提示词',\n 'settings.allowAgentImageGeneration': '允许 Agent 调用生图',\n 'settings.allowAgentImageGenerationHint': '默认开启。关闭后,Agent 无法提交、查询或取消生图任务;侧边栏工作台不受影响。',\n 'settings.enabled': '启用插件',\n 'settings.enabledHint': '关闭后生图面板不可用(设置卡片始终可用)',\n 'settings.save': '保存',\n 'settings.saving': '保存中…',\n 'settings.discard': '放弃修改',\n 'settings.unsaved': '有未保存的修改',\n 'settings.saveFailed': '保存失败,请重试',\n 'settings.readOnly': '当前设置文档为只读,无法保存。',\n 'settings.notExposed': '设置命名空间不可用:本部署未提供该插件的设置服务。',\n 'settings.expand': '展开',\n 'settings.collapse': '收起',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '重置',\n 'settings.invalidNumber': '请输入有效数字',\n\n\n\n // template library\n 'templates.open': '模板库',\n 'templates.title': '提示词模板库',\n 'templates.sources': '模板库来源',\n 'templates.meta': '共 {count} 个模板 · {origin}',\n 'templates.origin.bundled': '内置快照',\n 'templates.origin.refreshed': '在线刷新',\n 'templates.search': '搜索模板标题或提示词…',\n 'templates.all': '全部',\n 'templates.close': '关闭',\n 'templates.back': '返回列表',\n 'templates.use': '使用此提示词',\n 'templates.copy': '复制提示词',\n 'templates.copied': '已复制',\n 'templates.refresh': '刷新本库',\n 'templates.refreshing': '刷新中…',\n 'templates.refreshed': '已刷新,共 {count} 个模板',\n 'templates.refreshFailed': '刷新失败:{error}',\n 'templates.favorites': '收藏',\n 'templates.favoritesHint': '只看已收藏的模板',\n 'templates.favoritesEmpty': '还没有收藏,点击模板卡片右上角的星标即可收藏',\n 'templates.favoriteAdd': '收藏此模板',\n 'templates.favoriteRemove': '取消收藏',\n 'templates.favorite': '收藏',\n 'templates.unfavorite': '已收藏',\n 'templates.cacheAll': '缓存全部图片',\n 'templates.cacheAllHint': '通过本机代理把当前模板库的全部参考图缓存到本地磁盘,之后离线也能浏览',\n 'templates.caching': '缓存中 {done}/{total}…',\n 'templates.cached': '图片已全部缓存',\n 'templates.empty': '没有匹配的模板',\n 'templates.loading': '正在加载模板库…',\n 'templates.loadFailed': '模板库加载失败:{error}',\n 'templates.retry': '重试',\n 'templates.attribution': '模板与图片来自各来源站点,作者链接见模板详情',\n 'templates.source': '来源:{label}',\n 'templates.featured': '精选',\n // inspiration wall (studio empty state)\n 'inspiration.title': '灵感案例',\n 'inspiration.shuffle': '随机',\n 'inspiration.shuffling': '换一批…',\n 'inspiration.useHint': '点击使用该提示词',\n // channel management\n 'channels.title': '渠道',\n 'channels.hint': '填写各渠道的 API 地址与密钥即可使用对应模型',\n 'channels.empty': '尚无渠道,点击下方按钮添加',\n 'channels.addProvider': '添加提供方',\n 'channels.addCustom': '添加自定义渠道',\n 'channels.untitled': '未命名渠道',\n 'channels.keySet': '密钥已设置',\n 'channels.keyMissing': '未设置密钥',\n 'channels.modelCount': '{n} 个模型',\n 'channels.noModels': '未配置模型',\n 'channels.defaultLabel': '默认',\n 'channels.statusReady': '可用',\n 'channels.statusIncomplete': '配置未完成',\n 'channels.edit': '编辑',\n 'channels.delete': '删除',\n 'channels.deleteConfirmTitle': '删除渠道「{name}」?该渠道的密钥将被移除;历史与素材库中的记录仍会保留。',\n 'channels.confirm': '确认删除',\n 'channels.cancel': '取消',\n 'channels.editorTitle': '渠道',\n 'channels.editorSaveNote': '改动将随卡片底部「保存」一起生效',\n 'channels.displayName': '显示名称',\n 'channels.apiUrl': 'API 地址',\n 'channels.apiKey': 'API 密钥',\n 'channels.keyReplaceHint': '已配置 — 输入新值可替换',\n 'channels.keyMissingHint': '填写密钥',\n 'channels.keyClear': '清除密钥',\n 'channels.modelCatalogTitle': '模型目录',\n 'channels.noModelsHint': '暂无模型:点击「检测」拉取候选,或手动添加;别名(显示名)默认等于上游模型 id,可自行修改。',\n 'channels.detect': '重新检测',\n 'channels.detecting': '检测中…',\n 'channels.detectSuccess': '连通 ✓ · {n} 个候选',\n 'channels.detectFailed': '检测失败:{error}',\n 'channels.detectOk': '已连接该 API',\n 'channels.modelAliasLabel': '显示名(别名)',\n 'channels.modelIdLabel': '上游模型 id',\n 'channels.unknownProtocol': '未知协议,按通用 OpenAI 协议尝试',\n 'channels.removeModel': '移除模型',\n 'channels.manualAddPlaceholder': '输入模型 id,例如 qwen-image',\n 'channels.addModelConfirm': '添加',\n 'channels.copyFrom': '从其他渠道复制…',\n 'channels.copyApply': '复制',\n 'channels.candidatesTitle': '图片模型候选(点击加入)',\n 'channels.deleteThisChannel': '删除此渠道',\n 'channels.setDefault': '设为默认渠道',\n 'channels.presetPickerTitle': '添加提供方',\n 'channels.presetPickerHint': '选择一个内置提供方,仅需填写 API 密钥',\n 'channels.presetCustomHint': '自行填写 API 地址、密钥与模型目录',\n 'channels.presetLoadFailed': '加载提供方失败:{error}',\n 'canvas.skills.fileNode': '文件节点',\n 'canvas.skills.addFileNode': '添加文件节点',\n 'canvas.skills.emptyFileNode': '点击上传文件',\n 'canvas.skills.fileMenuUpload': '上传文件',\n 'canvas.skills.fileDropHint': '文件拖到此处即可上传',\n 'canvas.skills.fileDropSub': '支持 PDF / Office / 文本 / 图片等,≤50MB',\n 'canvas.skills.fileSizeUnknown': '大小未知',\n 'canvas.skills.filePreviewUnavailable': '此格式无法内联预览,请下载或用技能处理',\n 'canvas.skills.filePreviewLoading': '正在读取预览…',\n 'canvas.skills.fileDownload': '下载文件',\n 'canvas.skills.fileOpen': '用系统程序打开',\n 'canvas.skills.fileKind.text': '文本',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': '图片',\n 'canvas.skills.fileKind.office': '文档',\n 'canvas.skills.fileKind.archive': '压缩包',\n 'canvas.skills.fileKind.audio': '音频',\n 'canvas.skills.fileKind.video': '视频',\n 'canvas.skills.fileKind.other': '文件',\n 'canvas.skills.fileTooLarge': '文件超过 {size}MB 上限',\n 'canvas.skills.fileTypeBlocked': '出于安全考虑,不支持上传 {ext} 文件',\n 'canvas.skills.fileUploadFailed': '文件上传失败:{message}',\n 'canvas.preview.expand': '放大预览',\n 'canvas.preview.close': '关闭预览',\n 'canvas.preview.copy': '复制内容',\n 'canvas.preview.copied': '已复制',\n 'canvas.preview.openTab': '新标签页打开',\n 'canvas.preview.retry': '重试',\n 'canvas.preview.failed': '读取预览失败:{message}',\n 'canvas.preview.empty': '文件是空的',\n 'canvas.preview.textTruncated': '文件较大,仅显示前 {chars} 个字符',\n 'canvas.preview.tableTruncated': '表格较大,仅显示前 {rows} 行',\n 'canvas.preview.archiveTruncated': '压缩包条目较多,仅列出前 {count} 项',\n 'canvas.preview.docTruncated': '文档较长,仅显示前 {blocks} 段',\n 'canvas.preview.slidesTruncated': '幻灯片较多,仅显示前 {count} 页',\n 'canvas.preview.slideOf': '第 {page} 页',\n 'canvas.preview.rendered': '渲染',\n 'canvas.preview.source': '源码',\n 'canvas.preview.pdfFallback': '当前环境无法内联显示 PDF,请下载后查看',\n 'canvas.preview.pdfHeuristic': 'PDF 文字为启发式提取,复杂排版可能丢失顺序',\n 'canvas.preview.reason.unsupported': '这种格式没有可读内容,请下载后查看,或先转成图片 / 文本节点再交给技能处理',\n 'canvas.preview.reason.unreadable': '没能从这个文件里读出文字,可能是扫描件或特殊编码;可以转成图片节点交给视觉模型',\n 'canvas.preview.doubleClick': '双击放大预览',\n 'canvas.skills.button': '技能',\n 'canvas.skills.menuTitle': '运行技能',\n 'canvas.skills.runOn': '在「{name}」上运行技能',\n 'canvas.skills.pickerTitle': '选择技能',\n 'canvas.skills.searchPlaceholder': '搜索技能…',\n 'canvas.skills.groupBuiltin': '内置动作',\n 'canvas.skills.groupExternal': '本机技能',\n 'canvas.skills.emptyExternal': '本机没有可用技能,可在 ~/.dsh/skills 安装后重试',\n 'canvas.skills.empty': '没有可用技能',\n 'canvas.skills.loading': '正在读取技能列表…',\n 'canvas.skills.tierLight': '轻量',\n 'canvas.skills.tierHeavy': '重任务',\n 'canvas.skills.accepts': '可用节点:{types}',\n 'canvas.skills.incompatible': '该技能不适用于此节点类型',\n 'canvas.skills.disabled': '画布技能已在设置中关闭',\n 'canvas.skills.noRegistry': '本宿主未挂载技能注册表(ctx.skills),只能使用内置动作',\n 'canvas.skills.registryFailed': '读取技能列表失败:{message}',\n 'canvas.skills.needAgent': '需要宿主 Agent 运行层(本机 DSH 未提供),重任务技能不可用',\n 'canvas.skills.unknown': '技能不存在或已被移除',\n 'canvas.skills.canvasMissing': '画布不存在,请刷新后重试',\n 'canvas.skills.noInput': '这个节点没有可处理的输入',\n 'canvas.skills.noChat': '轻量技能需要先在「设置 → 插件 → AI 生图 → 提示词增强」配置聊天模型',\n 'canvas.skills.noDescription': '(技能未提供描述)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': '你在执行名为「{name}」的技能。严格按技能说明处理用户提供的输入,只用用户语言回答,直接输出处理结果,不要复述技能说明。',\n 'canvas.skills.externalHeader': '请按技能「{name}」的说明处理下面的输入。',\n 'canvas.skills.externalHeavyCost': '该技能属于长任务,会启动无头 Agent 执行,可能长时间占用模型额度。',\n 'canvas.skills.extraInstruction': '额外要求:',\n 'canvas.skills.inputsHeader': '输入内容:',\n 'canvas.skills.inputsEmpty': '(没有可读取的文本内容,请依据附带的图片处理)',\n 'canvas.skills.needInstruction': '请填写自定义润色指令',\n 'canvas.skills.needImage': '该技能需要图片节点作为输入',\n 'canvas.skills.needText': '该技能需要带文字的文本节点作为输入',\n 'canvas.skills.fileMissing': '文件资产不存在或已被清理',\n 'canvas.skills.extractFailed': '无法抽取该文件的内容',\n 'canvas.skills.nothingToRead': '没有可读取的文本内容',\n 'canvas.skills.truncated': '内容较长,已截断到 {count} 字符',\n 'canvas.skills.tooManyImages': '图片数量超过 {count} 张,只处理了前面的图片',\n 'canvas.skills.extractNodeTitle': '「{name}」内容',\n 'canvas.skills.summaryHead': '已汇总 {count} 个来源节点:',\n 'canvas.skills.describeAsk': '请描述这 {count} 张图片。',\n 'canvas.skills.polishNodeTitle': '润色结果({style})',\n 'canvas.skills.skillMissing': '本机未安装「{name}」技能。请先安装该技能(例如让 DSH Agent 安装 https://github.com/ningzimu/image-to-editable-ppt-skill ),再重试。',\n 'canvas.skills.agentFailed': '无法创建无头 Agent(请检查模型与 Agent 预设配置)',\n 'canvas.skills.needModel': '宿主没有提供默认模型(agent-default-model),重任务技能无法启动 Agent。请先在「设置 → 通用」选择默认模型,再重试。',\n 'canvas.skills.noOutputFile': '技能没有产出文件,已把 Agent 的回复保存为文本节点',\n 'canvas.skills.heavyIntro': '你正在为一个可视化画布执行技能「{name}」。严格按技能说明完成整个流程,不要省略步骤。',\n 'canvas.skills.heavyBodyHeader': '技能说明:',\n 'canvas.skills.heavyInputsHeader': '本任务的输入文件(绝对路径):',\n 'canvas.skills.heavyOutputHeader': '把最终产物写入这个目录:{dir}',\n 'canvas.skills.heavyRules': '要求:\\n1. 全部中间文件放在当前工作目录,最终产物只能放在上面的输出目录。\\n2. 完成后用简短中文回复:产物文件名、是否成功、注意事项。',\n 'canvas.skills.heavyAsk': '请执行技能「{name}」。',\n 'canvas.skills.heavyAskInputs': '输入文件目录:{dir}',\n 'canvas.skills.heavyAskOutput': '输出目录:{dir}',\n 'canvas.skills.heavyAskLoadSkill': '(如果技能说明不在上下文里,请先用技能名「{name}」加载它)',\n 'canvas.skills.stageQueued': '排队中',\n 'canvas.skills.stagePreparing': '准备输入文件',\n 'canvas.skills.stageReading': '读取节点内容',\n 'canvas.skills.stageRunning': '技能执行中',\n 'canvas.skills.stageCollecting': '收集产物',\n 'canvas.skills.stageDone': '完成',\n 'canvas.skills.stageCancelled': '已取消',\n 'canvas.skills.stagePrepared': '已备好 {count} 个输入文件',\n 'canvas.skills.stageArtifacts': '技能执行中 · 已产出 {count} 个文件',\n 'canvas.skills.runElapsed': '已用时',\n 'canvas.skills.runRetry': '重试',\n 'canvas.skills.runStarted': '技能「{name}」已开始',\n 'canvas.skills.runDone': '技能完成,新增 {count} 个节点',\n 'canvas.skills.runFailed': '技能运行失败:{message}',\n 'canvas.skills.runCancelled': '技能运行已取消',\n 'canvas.skills.cancel': '取消运行',\n 'canvas.skills.cancelling': '正在取消…',\n 'canvas.skills.confirmTitle': '运行重任务技能',\n 'canvas.skills.confirmBody': '技能「{name}」会启动一个无头 Agent 在本机执行,可能持续数分钟到数十分钟,并消耗较多模型额度。',\n 'canvas.skills.confirmCost': '成本提示:{hint}',\n 'canvas.skills.confirmRequires': '前置条件:{list}',\n 'canvas.skills.confirmRun': '确认运行',\n 'canvas.skills.confirmCancel': '先不运行',\n 'canvas.skills.batchTitle': '批量运行',\n 'canvas.skills.batchNote': '将依次在 {count} 个节点上运行(串行执行)',\n 'canvas.skills.batchTooMany': '一次最多处理 {count} 个节点',\n 'canvas.skills.sourceBadge': '技能产出',\n 'canvas.skills.sourceTip': '由技能「{name}」产出,来自 {count} 个来源节点',\n 'canvas.skills.rerun': '用同一技能再跑一次',\n 'canvas.skills.history': '技能运行记录',\n 'canvas.skills.historyEmpty': '暂无运行记录',\n 'canvas.skills.needConfirm': '该技能需要确认后才能运行',\n 'canvas.skills.polish': '文本润色',\n 'canvas.skills.polishDesc': '按风格改写文本节点:更正式 / 更口语 / 更短 / 扩写,或自定义指令',\n 'canvas.skills.describe': '图片描述',\n 'canvas.skills.describeDesc': '用视觉模型读出图片内容,产出文本节点(可用作提示词)',\n 'canvas.skills.extract': '内容抽取',\n 'canvas.skills.extractDesc': '把文件或图片里的文字抽成文本节点(文本 / 代码 / OOXML 文档 / PDF)',\n 'canvas.skills.ppt': '图片转可编辑 PPT',\n 'canvas.skills.pptDesc': '调用本机 image-to-editable-ppt 技能,把图片重建为可编辑 .pptx 文件节点',\n 'canvas.skills.pptCost': '长任务:10 页 PPT 可能消耗较多额度,单页通常 10 分钟以上',\n 'canvas.skills.pptRequires': '需要本机已安装 image-to-editable-ppt 技能,并按其文档配置 OCR Token 与图片后端',\n 'canvas.polish.button': 'AI 润色',\n 'canvas.polish.title': 'AI 润色',\n 'canvas.polish.formal': '更正式',\n 'canvas.polish.casual': '更口语',\n 'canvas.polish.shorter': '更短',\n 'canvas.polish.expand': '扩写',\n 'canvas.polish.custom': '自定义',\n 'canvas.polish.customPlaceholder': '例如:改写成小红书文案,带 emoji 和话题标签',\n 'canvas.polish.run': '开始润色',\n 'canvas.polish.previewTitle': '原 / 新对比',\n 'canvas.polish.apply': '应用',\n 'canvas.polish.revert': '还原',\n 'canvas.polish.applied': '已应用润色结果',\n 'canvas.polish.directHint': '当前设置:润色后直接覆盖原文(可在设置中改为先预览)',\n 'settings.skillsEnabled': '画布技能',\n 'canvas.skills.libraryButton': '技能库',\n 'canvas.skills.libraryTitle': '技能库管理',\n 'canvas.skills.libraryInstalled': '已安装技能({count})',\n 'canvas.skills.libraryEmpty': '本机还没有安装任何技能。可以从仓库链接安装,或上传技能压缩包。',\n 'canvas.skills.libraryRoot': '安装目录:{root}',\n 'canvas.skills.libraryInstall': '在线安装',\n 'canvas.skills.libraryInstallHint': '支持 GitHub 仓库 / 目录 / 单个 SKILL.md、任意 git 地址、zip 压缩包地址;多个地址用换行分隔。安装后宿主动态加载,无需重启。',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo 或 …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': '上传压缩包',\n 'canvas.skills.libraryUploadHint': '上传一个包含 SKILL.md 的 zip(单文件 ≤50MB),解压后安装到 ~/.dsh/skills。',\n 'canvas.skills.libraryDropzone': '点击选择或拖入 .zip 压缩包',\n 'canvas.skills.libraryForce': '覆盖已安装的同名技能',\n 'canvas.skills.libraryInstallAction': '安装',\n 'canvas.skills.libraryInstalling': '安装中…',\n 'canvas.skills.libraryRemove': '卸载',\n 'canvas.skills.libraryRemoving': '卸载中…',\n 'canvas.skills.libraryRemoveConfirm': '确定卸载技能「{name}」?该技能目录会被删除。',\n 'canvas.skills.libraryInstalledToast': '已安装技能:{names}',\n 'canvas.skills.libraryFailedToast': '安装失败:{message}',\n 'canvas.skills.libraryRemovedToast': '已卸载技能:{name}',\n 'canvas.skills.libraryUnavailable': '本宿主不支持管理技能库',\n 'canvas.skills.libraryReload': '刷新',\n 'canvas.skills.libraryOpenFolder': '打开技能目录',\n 'canvas.skills.installNow': '去安装',\n 'canvas.skills.needsInstall': '未安装',\n 'canvas.skills.missingHint': '技能「{name}」还没安装,点击安装即可使用',\n 'canvas.skills.installArchiveFailed': '压缩包安装失败:{message}',\n 'canvas.skills.libraryIssueFrontmatter': 'SKILL.md 缺少 YAML frontmatter(name / description 必填),本机技能注册表不会加载它,节点里也用不了',\n 'canvas.skills.libraryIssueName': 'frontmatter 的 name「{name}」不是 kebab-case(只能小写字母、数字和连字符),本机技能注册表会忽略它,节点里也用不了',\n 'canvas.skills.libraryIssueDescription': 'SKILL.md 的 frontmatter 缺少 description(必填),本机技能注册表不会加载它,节点里也用不了',\n 'canvas.skills.libraryIssueInvocation': 'disable-model-invocation / user-invocable 的写法无法解析,本机技能注册表会忽略它,节点里也用不了',\n 'canvas.skills.libraryIssueUserOnly': '该技能声明了 disable-model-invocation,只允许人工调用,画布节点无法运行它',\n // Skill configuration (declared in skill.config.json, see docs/skill-config.md)\n 'canvas.skills.configOpen': '配置',\n 'canvas.skills.configSave': '保存',\n 'canvas.skills.configSaveApply': '保存并应用',\n 'canvas.skills.configSaving': '保存中…',\n 'canvas.skills.configApplying': '应用中…',\n 'canvas.skills.configSaved': '已保存',\n 'canvas.skills.configSaveFailed': '保存失败',\n 'canvas.skills.configApplied': '已保存并应用',\n 'canvas.skills.configApplyFailed': '应用失败',\n 'canvas.skills.configClear': '清除',\n 'canvas.skills.configSecretSet': '已设置(留空表示不改动)',\n 'canvas.skills.configSecretUnset': '未设置',\n 'canvas.skills.configMissingHint': '需要配置:{fields}',\n 'canvas.skills.configGo': '去配置',\n 'canvas.skills.configSourceSkill': '配置项由该技能自己的 skill.config.json 声明',\n 'canvas.skills.configSourcePlugin': '配置项来自本插件内置的已知技能配方(技能自带声明会优先)',\n 'canvas.skills.configHeader': '已配置的值:',\n 'canvas.skills.configUnwritable': '本宿主无法写入设置,技能配置暂不可用',\n 'canvas.skills.configIssueVersion': 'skill.config.json 的 version 不是 1,已忽略这份声明',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json 无法解析(JSON 语法错误),已忽略这份声明',\n 'canvas.skills.configIssueEmpty': 'skill.config.json 里没有可用的字段或步骤,已忽略这份声明',\n} as const\n\nexport const en: Record<keyof typeof zh, string> = {\n 'entry.label': 'AI Image',\n 'entry.tooltip': 'AI image generation studio (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream family)',\n 'entry.newSession': 'New session',\n 'entry.newSessionTooltip': 'Create a new DSH session',\n 'entry.image': 'Image',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': 'Like it or have suggestions? Head to GitHub to open issues and star us!',\n 'conversation.add': 'Add to chat',\n 'conversation.adding': 'Adding…',\n 'conversation.added': 'Image added to the current chat',\n 'conversation.addHint': 'After adding to chat, use /edit_image to modify it; the command uses the plugin image model',\n 'conversation.noSession': 'Open a session before adding an image',\n 'conversation.unavailable': 'The current session is unavailable',\n 'conversation.busy': 'The chat is sending a message; try again shortly',\n 'mode.text': 'Text to Image',\n 'mode.edit': 'Image to Image',\n 'workspace.label': 'Workspace',\n 'workspace.normal': 'Standard',\n 'workspace.canvas': 'Infinite canvas',\n 'workspace.ecommerce': 'E-commerce',\n 'canvas.addImage': 'Add image',\n 'canvas.addText': 'Add text',\n 'canvas.generate': 'Generate image',\n 'canvas.addToCanvas': 'Add to canvas',\n 'canvas.untitled': 'Untitled canvas',\n 'canvas.imageNode': 'Image node',\n 'canvas.textNode': 'Text node',\n 'canvas.fromGallery': 'Gallery image',\n 'canvas.fromHistory': 'History image',\n 'canvas.project': 'Canvas project',\n 'canvas.newCanvas': 'New canvas',\n 'canvas.deleteCanvas': 'Delete canvas',\n 'canvas.deleteCanvasConfirm': 'Click again to confirm',\n 'canvas.rename': 'Rename canvas',\n 'canvas.renameHint': 'Double-click to rename',\n 'canvas.background': 'Canvas background',\n 'canvas.backgroundDots': 'Dots',\n 'canvas.backgroundLines': 'Grid lines',\n 'canvas.backgroundWaves': 'Waves',\n 'canvas.backgroundShapeGrid': 'Scrolling grid',\n 'canvas.backgroundDotField': 'Dot field',\n 'canvas.backgroundDotGrid': 'Interactive dots',\n 'canvas.backgroundFloatingLines': 'Floating lines',\n 'canvas.backgroundFlow': 'Fluid flow',\n 'canvas.backgroundLiquid': 'Liquid ether',\n 'canvas.backgroundFaultyTerminal': 'Faulty terminal',\n 'canvas.backgroundSilk': 'Silk',\n 'canvas.backgroundGalaxy': 'Galaxy',\n 'canvas.backgroundBlank': 'Blank',\n 'canvas.backgroundUpload': 'Upload custom background…',\n 'canvas.backgroundRemove': 'Remove custom background',\n 'canvas.templateLibrary': 'Template library (pick a prompt to create text + config nodes)',\n 'canvas.undo': 'Undo (Ctrl+Z)',\n 'canvas.redo': 'Redo (Ctrl+Shift+Z)',\n 'canvas.saving': 'Saving…',\n 'canvas.saved': 'Saved',\n 'canvas.saveFailed': 'Save failed',\n 'canvas.loading': 'Loading…',\n 'canvas.toolSelect': 'Select / move nodes',\n 'canvas.toolPan': 'Pan canvas (Space / Ctrl + drag)',\n 'canvas.delete': 'Delete',\n 'canvas.duplicate': 'Duplicate (Ctrl+D)',\n 'canvas.download': 'Download image',\n 'canvas.fitView': 'Fit all content',\n 'canvas.minimapOpen': 'Open minimap',\n 'canvas.minimapClose': 'Close minimap',\n 'canvas.minimap': 'Canvas minimap',\n 'canvas.zoom': 'Zoom',\n 'canvas.textPlaceholder': 'Type text…',\n 'canvas.generatingNode': 'Generating…',\n 'canvas.generateFailed': 'Generation failed',\n 'canvas.retry': 'Retry',\n 'canvas.taskLost': 'Generation task lost (service restarted); please retry',\n 'canvas.emptyImageNode': 'Click to upload an image',\n 'canvas.connectHint': 'Drag onto another node to connect',\n 'canvas.connectAddHint': 'Drag onto another node to connect, or click to add the next node',\n 'canvas.resizeHint': 'Drag to resize (image ratio locked)',\n 'canvas.count': 'Image count',\n 'canvas.configNode': 'Generation config',\n 'canvas.addConfigNode': 'Add config node',\n 'canvas.configHint': 'Connect image nodes on the left as references and text nodes as prompts; select this node to write the prompt in the composer below and generate.',\n 'canvas.composerLinked': '{count} input node(s) connected',\n 'canvas.composerPlaceholder': 'Describe the image to generate; or connect text nodes as the prompt…',\n 'canvas.countUnit': '{count}',\n 'canvas.model': 'Image model',\n 'canvas.modelPlaceholder': 'Choose a model',\n 'canvas.size': 'Size ratio',\n 'canvas.sizeAuto': 'Auto ratio',\n 'canvas.quality': 'Quality',\n 'canvas.qualityAuto': 'Auto quality',\n 'canvas.needApi': 'Configure the image API first',\n 'canvas.needPrompt': 'Enter a generation prompt first',\n 'canvas.needModel': 'No image model available',\n 'canvas.openSettings': 'Open settings',\n 'canvas.deleteConnection': 'Delete connection',\n 'canvas.paste': 'Paste (Ctrl+V)',\n 'canvas.addImageNode': 'Add image node',\n 'canvas.addTextNode': 'Add text node',\n 'canvas.addSketch': 'Add sketch board (draw-to-generate)',\n 'canvas.sketchNode': 'Sketch board',\n 'canvas.addSketchNode': 'Add sketch board node',\n 'canvas.sketchPlaceholder': 'Draw a sketch freely, then connect it to a config node as the reference image',\n 'canvas.sketchBrush': 'Brush',\n 'canvas.sketchEraser': 'Eraser',\n 'canvas.sketchUndo': 'Undo last stroke',\n 'canvas.sketchClear': 'Clear board',\n 'canvas.sketchWidth': 'Stroke width',\n 'canvas.dismiss': 'Dismiss',\n 'canvas.annotate': 'Annotate: draw a box to attach a prompt card',\n 'canvas.annotationNode': 'Annotation prompt',\n 'canvas.annotationTag': 'Annotated',\n 'canvas.annotationPlaceholder': 'Describe what this box should become, e.g. replace it with an orange cat',\n 'canvas.annotationDrawHint': 'Drag on the image to draw a box; releasing attaches a prompt card that follows the image (no connection needed)',\n 'canvas.removeBackground': 'Remove background (local matting, transparent PNG)',\n 'canvas.removingBackground': 'Removing background…',\n 'canvas.removeBgDone': 'Background removed ({percent}% transparent)',\n 'canvas.removeBgFailed': 'The background is too complex for local matting; box the subject with 标注 and let the model redraw it',\n 'canvas.alreadyTransparent': 'This image already has a transparent background',\n 'canvas.noBackgroundNode': 'Cut-out image',\n 'canvas.splitLayers': 'Split layers (AI decomposition into editable layers)',\n 'canvas.splittingLayers': 'Splitting layers…',\n 'canvas.layerSplitDone': 'Split into {count} layer node(s)',\n 'canvas.layerSplitEmpty': 'The model found no layers to split',\n 'canvas.layerBackground': 'Background · {label}',\n 'canvas.layerObject': 'Layer · {label}',\n 'canvas.layerText': 'Text · {label}',\n 'canvas.layerKindBackground': 'Background',\n 'canvas.layerKindObject': 'Object',\n 'canvas.layerKindText': 'Text',\n 'canvas.nodeModel': 'Model used by this node (retry and connected config nodes)',\n 'canvas.modelDefault': 'Default model',\n 'canvas.fontSmaller': 'Smaller font',\n 'canvas.fontLarger': 'Larger font',\n 'canvas.bold': 'Bold',\n 'canvas.textColor': 'Text color',\n 'canvas.colorDefault': 'Theme default',\n 'canvas.close': 'Close',\n 'canvas.tabUpload': 'Upload',\n 'canvas.tabHistory': 'History',\n 'canvas.tabGallery': 'Assets',\n 'canvas.imageMenuUpload': 'Local image',\n 'canvas.imageMenuAssets': 'Assets',\n 'canvas.imageMenuHistory': 'History',\n 'canvas.imageMenuGenerate': 'AI generate',\n 'canvas.tabGenerate': 'AI generate',\n 'canvas.dropHint': 'Drop images here to add them',\n 'canvas.dropSub': 'Multiple files, PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': 'Untitled work',\n 'canvas.picked': '{count} selected',\n 'canvas.generateAndAdd': 'Generate and add to canvas',\n 'chat.expand': 'Expand the conversation pane',\n 'chat.collapse': 'Collapse the conversation pane',\n 'chat.toggle': 'Chat',\n 'config.resizeHint': 'Drag to resize the parameter panel',\n 'ecommerce.generation': 'Generation settings',\n 'ecommerce.title': 'E-commerce Mode',\n 'ecommerce.badge': 'Beta',\n 'ecommerce.short': 'Store',\n 'ecommerce.anchorPending': 'Main image generated — remaining images are being generated with the main image as reference to keep the set consistent…',\n 'ecommerce.anchorFailed': 'Main image failed; remaining images are paused. Retry the main image or regenerate the set.',\n 'ecommerce.anchorNote': 'Anchored: main image first, remaining images reference it automatically for a consistent set',\n 'ecommerce.noAssetWarn': 'No product image uploaded: the main image will be generated from text as the anchor. Upload one for higher consistency.',\n 'ecommerce.product': 'Product details',\n 'ecommerce.productName': 'Product name (required)',\n 'ecommerce.projectName': 'Project name (optional)',\n 'ecommerce.constraints': 'Selling points & constraints',\n 'ecommerce.sellingPoints': 'Reference info: brand & model, specs (capacity/size/weight), materials, key selling points, certifications, accessories…',\n 'ecommerce.protectedFeatures': 'Features to preserve: color, logo, packaging text, structure',\n 'ecommerce.styleHint': 'Style hint, e.g. premium / realistic / natural light',\n 'ecommerce.setStructure': 'Image set structure',\n 'ecommerce.preview': 'Preview product set',\n 'ecommerce.planTitle': 'Plan to generate {count} images',\n 'ecommerce.planSlot': '{count} images · {description}',\n 'ecommerce.confirm': 'Confirm and generate set',\n 'ecommerce.uploadRef': 'Upload product images (required, up to 4: product / packaging / detail)',\n 'ecommerce.uploadShort': 'Upload',\n 'ecommerce.sellingTitle': 'Selling points',\n 'ecommerce.advanced': 'Advanced (platform / constraints)',\n 'ecommerce.params': 'Parameters',\n 'ecommerce.platformLabel': 'Platform',\n 'ecommerce.languageLabel': 'Copy language',\n 'ecommerce.customLanguageOption': 'Custom…',\n 'ecommerce.customLanguageLabel': 'Custom language',\n 'ecommerce.customLanguagePlaceholder': 'Enter a language, e.g. Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': 'Aspect ratio',\n 'ecommerce.categoryLabel': 'Category',\n 'ecommerce.multiSelect': 'multi-select',\n 'ecommerce.countHint': 'Click to adjust count (1-4)',\n 'ecommerce.refSettings': 'Reference images (optional)',\n 'ecommerce.styleTitle': 'Style / extra hints',\n 'ecommerce.protectedLabel': 'Features to preserve (optional)',\n 'ecommerce.assetsFull': 'Up to 4 product images',\n 'ecommerce.productRefTitle': 'Product images',\n 'ecommerce.productRefHint': 'Required · product shots, up to 4',\n 'ecommerce.styleRefTitle': 'Style reference',\n 'ecommerce.styleRefBadge': 'optional, 1 max',\n 'ecommerce.styleRefHint': 'Style, lighting, composition and mood only — the product itself cannot change.',\n 'ecommerce.styleRefUpload': 'Upload style reference',\n 'ecommerce.refInfoTitle': 'Product info',\n 'ecommerce.aiWrite': 'AI write',\n 'ecommerce.aiWriting': 'AI drafting…',\n 'ecommerce.aiWriteHint': 'Draft the product info prompt from name and category',\n 'ecommerce.promptInfoPlaceholder': 'Optional: brand & model, specs, materials, selling points, certifications, accessories, framing… or use AI write',\n 'ecommerce.optional': 'optional',\n 'ecommerce.previewBoardTitle': 'Pre-generation review',\n 'ecommerce.previewBoardHint': \"Tweak each image's prompt before submitting\",\n 'ecommerce.promptReset': 'Reset',\n 'ecommerce.promptAutoHint': 'Auto-generated · edit freely',\n 'ecommerce.refSelect': 'Reference image for this slot',\n 'ecommerce.refNone': 'No reference',\n 'ecommerce.role.product': 'Product',\n 'ecommerce.role.packaging': 'Packaging',\n 'ecommerce.role.detail': 'Detail / angle',\n 'ecommerce.role.style': 'Style',\n 'ecommerce.footerReady': '{count} images in this set',\n 'ecommerce.footerEmpty': 'Select the image set structure',\n 'ecommerce.results.title': 'Product set results',\n 'ecommerce.results.progress': '{done}/{total} completed',\n 'ecommerce.results.failed': '{count} failed',\n 'ecommerce.results.empty': 'After confirming, each image task and result will show here grouped by purpose',\n 'ecommerce.results.regenerate': 'Regenerate',\n 'ecommerce.results.export': 'Export manifest',\n 'ecommerce.results.newProduct': 'New product',\n 'prompt.placeholder': 'Describe the picture you want, e.g. an orange cat in an astronaut helmet raising a telescope on the moon, watercolor style, soft light…',\n 'prompt.required': 'Enter a prompt first',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': 'Over this model\\u2019s {limit}-character prompt limit; please shorten it',\n 'prompt.enhance': 'Enhance',\n 'prompt.enhancing': 'Enhancing…',\n 'prompt.enhanceHint': 'Expand the prompt with the configured chat model',\n 'prompt.configTitle': 'Configure a prompt enhancement model first',\n 'prompt.configHint': 'Settings has opened. Go to Plugins → AI Image, configure or reuse an API URL and key, choose a chat model, then save.',\n 'tasks.title': 'Generation tasks',\n 'tasks.queued': 'Queued',\n 'tasks.running': 'Generating',\n 'tasks.completed': 'Completed',\n 'tasks.failed': 'Failed',\n 'tasks.cancelled': 'Cancelled',\n 'tasks.cancel': 'Cancel',\n 'tasks.retry': 'Retry',\n 'params.size': 'Size',\n 'params.quality': 'Quality',\n 'params.count': 'Count',\n 'params.detail': 'Detail',\n 'size.auto': 'Auto',\n 'size.square': '1:1 Square',\n 'size.portrait34': '3:4 Portrait',\n 'size.landscape43': '4:3 Landscape',\n 'size.portrait916': '9:16 Vertical',\n 'size.portrait23': '2:3 Portrait',\n 'size.landscape32': '3:2 Landscape',\n 'size.wide169': '16:9 Widescreen',\n 'size.ultrawide21': '21:9 Ultrawide',\n 'quality.auto': 'Auto',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1',\n 'count.two': '2',\n 'count.three': '3',\n 'count.four': '4',\n 'detail.auto': 'Auto',\n 'detail.standard': 'Standard',\n 'detail.high': 'High',\n 'detail.hint': 'Passthrough parameter supported by some gpt-image-2 gateways; keep \"Auto\" for official endpoints',\n 'model.label': 'Model',\n 'model.channel': 'Channel',\n 'model.channelPlaceholder': 'Default channel',\n 'templates.favoritesCount': '{count} favorites',\n 'model.noEditModels': 'No model is available for this mode',\n 'compare.enable': 'Multi-model comparison',\n 'compare.models': 'Models to compare',\n 'compare.title': 'Multi-model comparison',\n 'compare.fullscreen': 'Fullscreen comparison',\n 'compare.selectRequired': 'Select at least one model',\n 'generate': 'Generate',\n 'generating': 'Generating…',\n 'edit.upload': 'Click or drag to upload a reference image',\n 'edit.uploadHint': 'PNG / JPG / WEBP, up to 10MB',\n 'edit.change': 'Change image',\n 'edit.remove': 'Remove',\n 'edit.required': 'Upload a reference image first',\n 'canvas.emptyTitle': 'Start creating',\n 'canvas.emptyHint': \"Double-click the canvas or use the bottom dock to add image/text nodes; wire image and text nodes into a target node, select it and generate from the composer below\",\n 'canvas.new': 'New creation',\n 'canvas.newHint': 'Start a new creation and clear the preview',\n 'canvas.error': 'Generation failed: {error}',\n 'canvas.submitting': 'Adding to the generation queue…',\n 'canvas.queued': 'Added to the generation queue',\n 'canvas.queueHint': '{count} task(s) waiting or generating',\n 'canvas.generating': 'Generating images…',\n 'canvas.elapsed': 'Elapsed {seconds}s',\n 'canvas.images': '{count} image(s) generated',\n 'download': 'Download',\n 'revisedPrompt': 'Refined prompt: {prompt}',\n 'history.title': 'History',\n 'history.empty': 'No history yet — generations will be saved here',\n 'history.clear': 'Clear',\n 'history.clearConfirm': 'Clear all history? This action cannot be undone.',\n 'history.restore': 'Restore',\n 'history.delete': 'Delete',\n 'history.images': 'images',\n 'history.viewing': 'History · {time}',\n 'history.search': 'Search prompt or model…',\n 'history.model': 'Model filter',\n 'history.ratio': 'Ratio filter',\n 'history.allModels': 'All models',\n 'history.allRatios': 'All ratios',\n 'gallery.title': 'Assets',\n 'gallery.categories': 'Categories',\n 'gallery.all': 'All assets',\n 'gallery.upload': 'Upload',\n 'gallery.uploadModel': 'Local upload',\n 'gallery.uploaded': 'Asset saved',\n 'gallery.uploadHint': 'Save local images for reuse on the canvas',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': 'Aspect ratio',\n 'gallery.tags': 'Tags',\n 'gallery.filterHint': 'Filter by mode, model, and aspect ratio',\n 'gallery.count': '· {count} works',\n 'gallery.viewMode': 'View mode',\n 'gallery.masonry': 'Masonry',\n 'gallery.grid': 'Grid',\n 'gallery.sort': 'Sort',\n 'gallery.newest': 'Newest',\n 'gallery.oldest': 'Oldest',\n 'gallery.untitled': 'Untitled work',\n 'gallery.search': 'Search works or models…',\n 'gallery.tagsPlaceholder': 'Tags, comma separated',\n 'gallery.tagsApply': 'Add tags',\n 'gallery.editTags': 'Edit tags',\n 'gallery.tagsEditShort': 'Edit',\n 'gallery.tagsSave': 'Save',\n 'gallery.tagsCancel': 'Cancel',\n 'gallery.selected': '{count} selected',\n 'gallery.selectionDone': 'Done selecting',\n 'gallery.selectionClear': 'Clear selection',\n 'gallery.downloadSelected': 'Download selected',\n 'gallery.exportJson': 'Export JSON',\n 'gallery.select': 'Select work',\n 'gallery.add': 'Add to gallery',\n 'gallery.added': 'Added to gallery',\n 'gallery.already': 'Already in gallery',\n 'gallery.delete': 'Remove',\n 'gallery.openFolder': 'Open folder',\n 'gallery.clear': 'Clear gallery',\n 'gallery.openFolderHint': 'Open the image save directory (~/.dsh/dsh-imagegen) in the system file manager',\n 'gallery.clearConfirm': 'Clear the entire gallery? This action cannot be undone.',\n 'gallery.empty': 'The gallery is empty — add images you like here',\n 'gallery.viewing': 'Gallery · {time}',\n 'preview.title': 'Image preview',\n 'preview.open': 'Click to preview',\n 'preview.close': 'Close',\n 'preview.prev': 'Previous',\n 'preview.next': 'Next',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': 'Image zoom controls',\n 'preview.zoomIn': 'Zoom in',\n 'preview.zoomOut': 'Zoom out',\n 'preview.zoomReset': 'Reset zoom',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': 'Copy prompt',\n 'preview.copied': 'Copied',\n 'preview.addToEdit': 'Add to image to image',\n 'config.missing': 'API not configured: open \"Settings → Plugins → Configurable\" and fill in api_url and api_key for AI Image.',\n 'config.generationTitle': 'Configure the image API first',\n 'config.generationHint': 'DSH Settings → Plugins → AI Image has opened. Enter the image API URL and API key, then save before generating.',\n 'config.enhancementTitle': 'Configure a prompt enhancement model first',\n 'config.enhancementHint': 'DSH Settings → Plugins → AI Image has opened. Configure or reuse a chat API URL and key, choose a chat model, then save.',\n 'config.disabledTitle': 'The AI Image plugin is disabled',\n 'config.disabledHint': 'DSH Settings → Plugins → AI Image has opened. Enable the plugin, then generate images.',\n 'config.configured': 'Connected to {url}',\n 'config.disabled': 'The plugin is disabled — re-enable it in Settings.',\n 'connection.connected': 'Connected',\n 'connection.disconnected': 'Disconnected',\n 'panel.collapseConfig': 'Collapse settings panel',\n 'panel.expandConfig': 'Expand settings panel',\n 'update.available': 'A new version is available: {version}',\n 'update.install': 'Update online',\n 'update.installing': 'Updating…',\n 'update.success': 'Updated to {version}; restart DSH to load it',\n 'update.failed': 'Update failed; please try again',\n 'update.release': 'View Release',\n 'settings.title': 'AI Image (dsh-imagegen)',\n 'settings.description': 'Configure the image generation API endpoint and key',\n 'settings.summary': 'Configure image-generation channels, models, and plugin switches',\n 'settings.currentVersion': 'Current version',\n 'settings.apiUrl': 'API URL (api_url)',\n 'settings.apiUrlHint': 'OpenAI-compatible base URL, e.g. https://api.openai.com/v1; /images/generations and /images/edits are appended',\n 'settings.apiKey': 'API Key (api_key)',\n 'settings.apiKeyHint': 'Bearer key, stored in plaintext in the local settings document; the UI only shows whether it is set',\n 'settings.apiKeySet': 'A key is stored; type a new value to replace it, or click \"Clear\" to remove it',\n 'settings.apiKeyClear': 'Clear',\n 'settings.imageModelsTitle': 'Image generation models',\n 'settings.imageModelsHint': 'Save the API URL and key, then detect candidate models. Select only models that actually support image generation.',\n 'settings.imageModels': 'Allowed image models',\n 'settings.imageModelsManualHint': 'One model per line. Add models manually when the API does not provide /models. The panel and Agent can use only this list.',\n 'settings.imageModelsFetch': 'Detect available models',\n 'settings.imageModelsLoading': 'Detecting…',\n 'settings.imageModelsCandidates': 'Detected candidate models (select, then save)',\n 'settings.addModel': '+ Add manually',\n 'settings.cancelAddModel': 'Hide add',\n 'settings.addModelPlaceholder': 'Enter a model name, e.g. qwen-image',\n 'settings.addModelConfirm': 'Add',\n 'settings.removeModel': 'Remove model',\n 'settings.optional': 'Optional',\n 'settings.moreOptions': 'More settings',\n 'settings.promptEnhanceTitle': 'Prompt enhancement model',\n 'settings.promptEnhanceHint': 'Expands short requests into complete image prompts. Blank URL and key reuse the image API configuration.',\n 'settings.promptApiUrl': 'Chat API URL (optional)',\n 'settings.promptApiUrlHint': 'OpenAI-compatible base URL. Leave blank to reuse the image API URL.',\n 'settings.promptApiKey': 'Chat API key (optional)',\n 'settings.promptApiKeyHint': 'Leave blank to reuse the image API key.',\n 'settings.promptModel': 'Chat model',\n 'settings.promptModelHint': 'Select or enter a model supporting /chat/completions.',\n 'settings.promptModelDetectionHint': 'Uses the image API by default. Detect and select one model.',\n 'settings.promptModelsFetch': 'Fetch available models',\n 'settings.promptModelsLoading': 'Fetching…',\n 'settings.promptModelsSelect': 'Select a model',\n 'settings.promptModelsCandidates': 'Detected chat model candidates',\n 'settings.addPromptModelPlaceholder': 'Enter a chat model name, e.g. gpt-4.1-mini',\n 'settings.promptApiAdvanced': 'Use a separate chat API (optional)',\n 'settings.announceToAgent': 'Announce this plugin to agents',\n 'settings.announceToAgentHint': 'When on, the plugin presence and capabilities are written into every agent system prompt',\n 'settings.allowAgentImageGeneration': 'Allow agents to generate images',\n 'settings.allowAgentImageGenerationHint': 'On by default. When off, agents cannot submit, query, or cancel image tasks; the sidebar studio remains available.',\n 'settings.enabled': 'Enable plugin',\n 'settings.enabledHint': 'When off, the generation studio is unavailable (this card stays available)',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving…',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved changes',\n 'settings.saveFailed': 'Save failed, please retry',\n 'settings.readOnly': 'The settings document is read-only; saving is disabled.',\n 'settings.notExposed': 'Settings namespace unavailable: this deployment does not serve this plugin\\'s settings.',\n 'settings.expand': 'Expand',\n 'settings.collapse': 'Collapse',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset',\n 'settings.invalidNumber': 'Enter a valid number',\n\n\n\n // template library\n 'templates.open': 'Templates',\n 'templates.title': 'Prompt Template Library',\n 'templates.sources': 'Template sources',\n 'templates.meta': '{count} templates · {origin}',\n 'templates.origin.bundled': 'bundled snapshot',\n 'templates.origin.refreshed': 'refreshed online',\n 'templates.search': 'Search template titles or prompts…',\n 'templates.all': 'All',\n 'templates.close': 'Close',\n 'templates.back': 'Back to list',\n 'templates.use': 'Use this prompt',\n 'templates.copy': 'Copy prompt',\n 'templates.copied': 'Copied',\n 'templates.refresh': 'Refresh this library',\n 'templates.refreshing': 'Refreshing…',\n 'templates.refreshed': 'Refreshed — {count} templates',\n 'templates.refreshFailed': 'Refresh failed: {error}',\n 'templates.favorites': 'Favorites',\n 'templates.favoritesHint': 'Show favorited templates only',\n 'templates.favoritesEmpty': 'No favorites yet — tap the star in a card\\'s top-right corner to save one',\n 'templates.favoriteAdd': 'Favorite this template',\n 'templates.favoriteRemove': 'Remove from favorites',\n 'templates.favorite': 'Favorite',\n 'templates.unfavorite': 'Favorited',\n 'templates.cacheAll': 'Cache all images',\n 'templates.cacheAllHint': 'Mirror every reference image of the current library to local disk through the host proxy, for offline browsing',\n 'templates.caching': 'Caching {done}/{total}…',\n 'templates.cached': 'All images cached',\n 'templates.empty': 'No matching templates',\n 'templates.loading': 'Loading the template library…',\n 'templates.loadFailed': 'Failed to load the library: {error}',\n 'templates.retry': 'Retry',\n 'templates.attribution': 'Templates and images come from each source site; author links are on each template',\n 'templates.source': 'Source: {label}',\n 'templates.featured': 'Featured',\n // inspiration wall (studio empty state)\n 'inspiration.title': 'Inspiration', 'inspiration.shuffle': 'Shuffle',\n 'inspiration.shuffling': 'Shuffling…',\n 'inspiration.useHint': 'Click to use this prompt',\n // channel management\n 'channels.title': 'Channels',\n 'channels.hint': 'Fill in each channel\\'s API URL and key to use its models',\n 'channels.empty': 'No channels yet — add one below',\n 'channels.addProvider': 'Add provider',\n 'channels.addCustom': 'Add custom channel',\n 'channels.untitled': 'Untitled channel',\n 'channels.keySet': 'Key set',\n 'channels.keyMissing': 'Key missing',\n 'channels.modelCount': '{n} model(s)',\n 'channels.noModels': 'No models',\n 'channels.defaultLabel': 'Default',\n 'channels.statusReady': 'Ready',\n 'channels.statusIncomplete': 'Config incomplete',\n 'channels.edit': 'Edit',\n 'channels.delete': 'Delete',\n 'channels.deleteConfirmTitle': 'Delete channel \"{name}\"? Its key will be removed; history and gallery records are kept.',\n 'channels.confirm': 'Delete',\n 'channels.cancel': 'Cancel',\n 'channels.editorTitle': 'Channel',\n 'channels.editorSaveNote': 'Changes take effect with the card\\'s \"Save\" button',\n 'channels.displayName': 'Display name',\n 'channels.apiUrl': 'API URL',\n 'channels.apiKey': 'API key',\n 'channels.keyReplaceHint': 'Configured — type a new value to replace it',\n 'channels.keyMissingHint': 'Enter a key',\n 'channels.keyClear': 'Clear key',\n 'channels.modelCatalogTitle': 'Model catalog',\n 'channels.noModelsHint': 'No models yet: run \"Detect\" to pull candidates, or add one manually; the alias (display name) defaults to the upstream model id and can be renamed.',\n 'channels.detect': 'Re-detect',\n 'channels.detecting': 'Detecting…',\n 'channels.detectSuccess': 'Connected ✓ · {n} candidates',\n 'channels.detectFailed': 'Detection failed: {error}',\n 'channels.detectOk': 'Connected to this API',\n 'channels.modelAliasLabel': 'Display name (alias)',\n 'channels.modelIdLabel': 'Upstream model id',\n 'channels.unknownProtocol': 'Unknown protocol — best-effort OpenAI',\n 'channels.removeModel': 'Remove model',\n 'channels.manualAddPlaceholder': 'Enter a model id, e.g. qwen-image',\n 'channels.addModelConfirm': 'Add',\n 'channels.copyFrom': 'Copy from another channel…',\n 'channels.copyApply': 'Copy',\n 'channels.candidatesTitle': 'Image model candidates (click to add)',\n 'channels.deleteThisChannel': 'Delete this channel',\n 'channels.setDefault': 'Set as default channel',\n 'channels.presetPickerTitle': 'Add provider',\n 'channels.presetPickerHint': 'Pick a built-in provider — only the API key is needed',\n 'channels.presetCustomHint': 'Configure the API URL, key, and model catalog yourself',\n 'channels.presetLoadFailed': 'Failed to load providers: {error}',\n 'canvas.skills.fileNode': 'File node',\n 'canvas.skills.addFileNode': 'Add file node',\n 'canvas.skills.emptyFileNode': 'Click to upload a file',\n 'canvas.skills.fileMenuUpload': 'Upload file',\n 'canvas.skills.fileDropHint': 'Drop a file here to upload',\n 'canvas.skills.fileDropSub': 'PDF / Office / text / images, up to 50MB',\n 'canvas.skills.fileSizeUnknown': 'Unknown size',\n 'canvas.skills.filePreviewUnavailable': 'No inline preview for this format — download it or run a skill',\n 'canvas.skills.filePreviewLoading': 'Loading preview…',\n 'canvas.skills.fileDownload': 'Download file',\n 'canvas.skills.fileOpen': 'Open with the system app',\n 'canvas.skills.fileKind.text': 'Text',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': 'Image',\n 'canvas.skills.fileKind.office': 'Document',\n 'canvas.skills.fileKind.archive': 'Archive',\n 'canvas.skills.fileKind.audio': 'Audio',\n 'canvas.skills.fileKind.video': 'Video',\n 'canvas.skills.fileKind.other': 'File',\n 'canvas.skills.fileTooLarge': 'The file exceeds the {size}MB limit',\n 'canvas.skills.fileTypeBlocked': '{ext} files cannot be uploaded for safety reasons',\n 'canvas.skills.fileUploadFailed': 'Upload failed: {message}',\n 'canvas.preview.expand': 'Open preview',\n 'canvas.preview.close': 'Close preview',\n 'canvas.preview.copy': 'Copy content',\n 'canvas.preview.copied': 'Copied',\n 'canvas.preview.openTab': 'Open in a new tab',\n 'canvas.preview.retry': 'Retry',\n 'canvas.preview.failed': 'Preview failed: {message}',\n 'canvas.preview.empty': 'The file is empty',\n 'canvas.preview.textTruncated': 'Large file — showing the first {chars} characters',\n 'canvas.preview.tableTruncated': 'Large table — showing the first {rows} rows',\n 'canvas.preview.archiveTruncated': 'Many entries — listing the first {count}',\n 'canvas.preview.docTruncated': 'Long document — showing the first {blocks} blocks',\n 'canvas.preview.slidesTruncated': 'Large deck — showing the first {count} slides',\n 'canvas.preview.slideOf': 'Slide {page}',\n 'canvas.preview.rendered': 'Rendered',\n 'canvas.preview.source': 'Source',\n 'canvas.preview.pdfFallback': 'This environment cannot display the PDF inline — download it instead',\n 'canvas.preview.pdfHeuristic': 'PDF text is extracted heuristically; complex layouts may lose their order',\n 'canvas.preview.reason.unsupported': 'No readable content for this format — download it, or turn it into an image or text node for a skill',\n 'canvas.preview.reason.unreadable': 'No text could be read from this file (a scan or an unusual encoding); convert it to an image node for a vision model',\n 'canvas.preview.doubleClick': 'Double-click to open the preview',\n 'canvas.skills.button': 'Skill',\n 'canvas.skills.menuTitle': 'Run a skill',\n 'canvas.skills.runOn': 'Run a skill on “{name}”',\n 'canvas.skills.pickerTitle': 'Pick a skill',\n 'canvas.skills.searchPlaceholder': 'Search skills…',\n 'canvas.skills.groupBuiltin': 'Built-in actions',\n 'canvas.skills.groupExternal': 'Local skills',\n 'canvas.skills.emptyExternal': 'No local skills found — install one under ~/.dsh/skills and reload',\n 'canvas.skills.empty': 'No skills available',\n 'canvas.skills.loading': 'Loading skills…',\n 'canvas.skills.tierLight': 'Quick',\n 'canvas.skills.tierHeavy': 'Long task',\n 'canvas.skills.accepts': 'Works on: {types}',\n 'canvas.skills.incompatible': 'This skill does not apply to this node type',\n 'canvas.skills.disabled': 'Canvas skills are disabled in the plugin settings',\n 'canvas.skills.noRegistry': 'This host mounted no skill registry (ctx.skills), so only built-in actions are available',\n 'canvas.skills.registryFailed': 'Could not read the skill list: {message}',\n 'canvas.skills.needAgent': 'Needs the host agent runtime, which this DSH build does not provide — long-task skills stay disabled',\n 'canvas.skills.unknown': 'That skill no longer exists',\n 'canvas.skills.canvasMissing': 'The canvas no longer exists — reload and try again',\n 'canvas.skills.noInput': 'This node has no processable input',\n 'canvas.skills.noChat': 'Quick skills need a chat model: Settings → Plugins → AI Image → Prompt enhancement',\n 'canvas.skills.noDescription': '(no description provided)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': 'You are running the skill “{name}”. Follow its instructions exactly on the input below, answer in the input language, and output only the result — never restate the instructions.',\n 'canvas.skills.externalHeader': 'Apply the skill “{name}” to the input below.',\n 'canvas.skills.externalHeavyCost': 'This is a long task: it runs a headless agent and may consume a large amount of model quota.',\n 'canvas.skills.extraInstruction': 'Additional requirement:',\n 'canvas.skills.inputsHeader': 'Input:',\n 'canvas.skills.inputsEmpty': '(no readable text — work from the attached images)',\n 'canvas.skills.needInstruction': 'Enter a custom polish instruction',\n 'canvas.skills.needImage': 'This skill needs an image node as input',\n 'canvas.skills.needText': 'This skill needs a text node with content as input',\n 'canvas.skills.fileMissing': 'The file asset is missing or was cleaned up',\n 'canvas.skills.extractFailed': 'Could not extract any content from this file',\n 'canvas.skills.nothingToRead': 'Nothing readable found',\n 'canvas.skills.truncated': 'Content was long and got truncated to {count} characters',\n 'canvas.skills.tooManyImages': 'More than {count} images were selected; only the first ones were processed',\n 'canvas.skills.extractNodeTitle': 'Content of “{name}”',\n 'canvas.skills.summaryHead': 'Collected {count} source nodes:',\n 'canvas.skills.describeAsk': 'Describe these {count} image(s).',\n 'canvas.skills.polishNodeTitle': 'Polished ({style})',\n 'canvas.skills.skillMissing': 'The skill “{name}” is not installed. Install it first (for example, ask your DSH agent to install https://github.com/ningzimu/image-to-editable-ppt-skill) and retry.',\n 'canvas.skills.agentFailed': 'Could not create a headless agent — check the model and agent preset configuration',\n 'canvas.skills.needModel': 'This host publishes no default model (agent-default-model), so a long-task skill cannot start its agent. Pick a default model in Settings → General and retry.',\n 'canvas.skills.noOutputFile': 'The skill produced no file; the agent’s answer was saved as a text node',\n 'canvas.skills.heavyIntro': 'You are running the skill “{name}” for a visual canvas. Complete the whole procedure exactly as documented; do not skip steps.',\n 'canvas.skills.heavyBodyHeader': 'Skill instructions:',\n 'canvas.skills.heavyInputsHeader': 'Input files for this task (absolute paths):',\n 'canvas.skills.heavyOutputHeader': 'Write the final artifact(s) into this directory: {dir}',\n 'canvas.skills.heavyRules': 'Requirements:\\n1. Keep every intermediate file in the working directory; the final artifact may only go into the output directory above.\\n2. When done, reply briefly with the artifact file names, whether it succeeded, and any caveats.',\n 'canvas.skills.heavyAsk': 'Run the skill “{name}”.',\n 'canvas.skills.heavyAskInputs': 'Input directory: {dir}',\n 'canvas.skills.heavyAskOutput': 'Output directory: {dir}',\n 'canvas.skills.heavyAskLoadSkill': '(If the skill instructions are not in context, load them by the skill name “{name}” first.)',\n 'canvas.skills.stageQueued': 'Queued',\n 'canvas.skills.stagePreparing': 'Preparing input files',\n 'canvas.skills.stageReading': 'Reading node content',\n 'canvas.skills.stageRunning': 'Running the skill',\n 'canvas.skills.stageCollecting': 'Collecting artifacts',\n 'canvas.skills.stageDone': 'Done',\n 'canvas.skills.stageCancelled': 'Cancelled',\n 'canvas.skills.stagePrepared': 'Prepared {count} input file(s)',\n 'canvas.skills.stageArtifacts': 'Running — {count} file(s) produced',\n 'canvas.skills.runElapsed': 'Elapsed',\n 'canvas.skills.runRetry': 'Retry',\n 'canvas.skills.runStarted': 'Skill “{name}” started',\n 'canvas.skills.runDone': 'Skill finished — {count} new node(s)',\n 'canvas.skills.runFailed': 'The skill failed: {message}',\n 'canvas.skills.runCancelled': 'The skill run was cancelled',\n 'canvas.skills.cancel': 'Cancel run',\n 'canvas.skills.cancelling': 'Cancelling…',\n 'canvas.skills.confirmTitle': 'Run a long-task skill',\n 'canvas.skills.confirmBody': '“{name}” starts a headless agent on this machine. It can run for minutes to tens of minutes and consume a large amount of model quota.',\n 'canvas.skills.confirmCost': 'Cost: {hint}',\n 'canvas.skills.confirmRequires': 'Requires: {list}',\n 'canvas.skills.confirmRun': 'Run it',\n 'canvas.skills.confirmCancel': 'Not now',\n 'canvas.skills.batchTitle': 'Batch run',\n 'canvas.skills.batchNote': 'Runs once per node, one after another ({count} nodes)',\n 'canvas.skills.batchTooMany': 'At most {count} nodes per run',\n 'canvas.skills.sourceBadge': 'Skill output',\n 'canvas.skills.sourceTip': 'Produced by the skill “{name}” from {count} source node(s)',\n 'canvas.skills.rerun': 'Run the same skill again',\n 'canvas.skills.history': 'Skill run history',\n 'canvas.skills.historyEmpty': 'No runs yet',\n 'canvas.skills.needConfirm': 'This skill needs confirmation before it runs',\n 'canvas.skills.polish': 'Polish text',\n 'canvas.skills.polishDesc': 'Rewrite a text node in a chosen style: more formal / casual / shorter / expanded, or a custom instruction',\n 'canvas.skills.describe': 'Describe image',\n 'canvas.skills.describeDesc': 'Read an image with a vision model and produce a text node (usable as a prompt)',\n 'canvas.skills.extract': 'Extract content',\n 'canvas.skills.extractDesc': 'Pull the text out of a file or image into a text node (text / code / OOXML / PDF)',\n 'canvas.skills.ppt': 'Image → editable PPT',\n 'canvas.skills.pptDesc': 'Drive the local image-to-editable-ppt skill to rebuild images into an editable .pptx file node',\n 'canvas.skills.pptCost': 'Long task: a 10-page deck can consume a large quota; a single page often takes 10+ minutes',\n 'canvas.skills.pptRequires': 'Needs the image-to-editable-ppt skill installed locally, with the OCR token and image backend configured as its docs describe',\n 'canvas.polish.button': 'AI polish',\n 'canvas.polish.title': 'AI polish',\n 'canvas.polish.formal': 'More formal',\n 'canvas.polish.casual': 'More casual',\n 'canvas.polish.shorter': 'Shorter',\n 'canvas.polish.expand': 'Expand',\n 'canvas.polish.custom': 'Custom',\n 'canvas.polish.customPlaceholder': 'e.g. rewrite as a social post with emoji and hashtags',\n 'canvas.polish.run': 'Polish',\n 'canvas.polish.previewTitle': 'Before / after',\n 'canvas.polish.apply': 'Apply',\n 'canvas.polish.revert': 'Revert',\n 'canvas.polish.applied': 'Polish applied',\n 'canvas.polish.directHint': 'Current setting: polish overwrites the node directly (switch to preview-first in the settings)',\n 'settings.skillsEnabled': 'Canvas skills',\n 'canvas.skills.libraryButton': 'Skill library',\n 'canvas.skills.libraryTitle': 'Skill library',\n 'canvas.skills.libraryInstalled': 'Installed skills ({count})',\n 'canvas.skills.libraryEmpty': 'No skills installed yet. Install one from a repository URL, or upload a skill archive.',\n 'canvas.skills.libraryRoot': 'Install root: {root}',\n 'canvas.skills.libraryInstall': 'Install from URL',\n 'canvas.skills.libraryInstallHint': 'GitHub repo / folder / single SKILL.md, any git URL, or a zip URL; separate several with new lines. The host loads installs live — no restart.',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo or …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': 'Upload archive',\n 'canvas.skills.libraryUploadHint': 'Upload a zip that contains SKILL.md (≤50MB); it is unpacked into ~/.dsh/skills.',\n 'canvas.skills.libraryDropzone': 'Click or drop a .zip archive here',\n 'canvas.skills.libraryForce': 'Replace an installed skill of the same name',\n 'canvas.skills.libraryInstallAction': 'Install',\n 'canvas.skills.libraryInstalling': 'Installing…',\n 'canvas.skills.libraryRemove': 'Uninstall',\n 'canvas.skills.libraryRemoving': 'Uninstalling…',\n 'canvas.skills.libraryRemoveConfirm': 'Uninstall “{name}”? Its folder is deleted from the skill library.',\n 'canvas.skills.libraryInstalledToast': 'Installed: {names}',\n 'canvas.skills.libraryFailedToast': 'Install failed: {message}',\n 'canvas.skills.libraryRemovedToast': 'Uninstalled: {name}',\n 'canvas.skills.libraryUnavailable': 'This host cannot manage the skill library',\n 'canvas.skills.libraryReload': 'Refresh',\n 'canvas.skills.libraryOpenFolder': 'Open skill folder',\n 'canvas.skills.installNow': 'Install',\n 'canvas.skills.needsInstall': 'Not installed',\n 'canvas.skills.missingHint': '“{name}” is not installed yet — install it to use this action',\n 'canvas.skills.installArchiveFailed': 'Archive install failed: {message}',\n 'canvas.skills.libraryIssueFrontmatter': 'No YAML frontmatter (name / description are required), so the host skill registry never loads it and no node can run it',\n 'canvas.skills.libraryIssueName': 'The frontmatter name “{name}” is not kebab-case (lowercase letters, digits and hyphens), so the host skill registry ignores it and no node can run it',\n 'canvas.skills.libraryIssueDescription': 'The SKILL.md frontmatter has no description (required), so the host skill registry never loads it and no node can run it',\n 'canvas.skills.libraryIssueInvocation': 'The disable-model-invocation / user-invocable spelling cannot be parsed, so the host skill registry ignores it and no node can run it',\n 'canvas.skills.libraryIssueUserOnly': 'This skill declares disable-model-invocation: it is human-invocable only, so canvas nodes cannot run it',\n 'canvas.skills.configOpen': 'Configure',\n 'canvas.skills.configSave': 'Save',\n 'canvas.skills.configSaveApply': 'Save and apply',\n 'canvas.skills.configSaving': 'Saving…',\n 'canvas.skills.configApplying': 'Applying…',\n 'canvas.skills.configSaved': 'Saved',\n 'canvas.skills.configSaveFailed': 'Could not save',\n 'canvas.skills.configApplied': 'Saved and applied',\n 'canvas.skills.configApplyFailed': 'Could not apply',\n 'canvas.skills.configClear': 'Clear',\n 'canvas.skills.configSecretSet': 'Set (leave empty to keep it)',\n 'canvas.skills.configSecretUnset': 'Not set',\n 'canvas.skills.configMissingHint': 'Needs configuration: {fields}',\n 'canvas.skills.configGo': 'Configure',\n 'canvas.skills.configSourceSkill': 'These fields are declared by the skill’s own skill.config.json',\n 'canvas.skills.configSourcePlugin': 'These fields come from this plugin’s built-in recipe for a known skill (a skill’s own declaration wins)',\n 'canvas.skills.configHeader': 'Already configured:',\n 'canvas.skills.configUnwritable': 'This host cannot write settings, so skill configuration is unavailable',\n 'canvas.skills.configIssueVersion': 'skill.config.json declares a version other than 1, so the declaration was ignored',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json could not be parsed (JSON syntax error), so the declaration was ignored',\n 'canvas.skills.configIssueEmpty': 'skill.config.json declares no usable field or step, so it was ignored',\n}\n\n/** Locale key union. */\nexport type ImageGenKey = keyof typeof zh\n\n/** Russian mirror of every key (полный словарь интерфейса). */\nexport const ru: Record<keyof typeof zh, string> = {\n 'entry.label': 'ИИ-генерация',\n 'entry.tooltip': 'Панель ИИ-генерации изображений (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream)',\n 'entry.newSession': 'Новая сессия',\n 'entry.newSessionTooltip': 'Создать новую сессию DSH',\n 'entry.image': 'Генерация',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': 'Понравился плагин или есть идеи? Поставьте звезду или откройте issue на GitHub!',\n 'conversation.add': 'В диалог',\n 'conversation.adding': 'Добавляем…',\n 'conversation.added': 'Изображение добавлено в текущий диалог',\n 'conversation.addHint': 'После добавления можно править через /edit_image; команда использует модель плагина',\n 'conversation.noSession': 'Сначала откройте сессию, затем добавьте изображение',\n 'conversation.unavailable': 'Текущая сессия недоступна',\n 'conversation.busy': 'Диалог отправляет сообщение, попробуйте позже',\n 'mode.text': 'Из текста',\n 'mode.edit': 'Из изображения',\n 'workspace.label': 'Режим работы',\n 'workspace.normal': 'Генерация',\n 'workspace.canvas': 'Бесконечный холст',\n 'workspace.ecommerce': 'E-commerce',\n 'canvas.addImage': 'Добавить изображение',\n 'canvas.addText': 'Добавить текст',\n 'canvas.generate': 'Создать изображение',\n 'canvas.addToCanvas': 'Добавить на холст',\n 'canvas.untitled': 'Безымянный холст',\n 'canvas.imageNode': 'Узел изображения',\n 'canvas.textNode': 'Текстовый узел',\n 'canvas.fromGallery': 'Из галереи',\n 'canvas.fromHistory': 'Из истории',\n 'canvas.project': 'Проект холста',\n 'canvas.newCanvas': 'Новый холст',\n 'canvas.deleteCanvas': 'Удалить холст',\n 'canvas.deleteCanvasConfirm': 'Нажмите ещё раз для подтверждения',\n 'canvas.rename': 'Переименовать холст',\n 'canvas.renameHint': 'Двойной клик — переименовать',\n 'canvas.background': 'Фон холста',\n 'canvas.backgroundDots': 'Точки',\n 'canvas.backgroundLines': 'Сетка',\n 'canvas.backgroundWaves': 'Волны',\n 'canvas.backgroundShapeGrid': 'Бегущая сетка',\n 'canvas.backgroundDotField': 'Поле точек',\n 'canvas.backgroundDotGrid': 'Интерактивные точки',\n 'canvas.backgroundFloatingLines': 'Плавающие линии',\n 'canvas.backgroundFlow': 'Жидкостный поток',\n 'canvas.backgroundLiquid': 'Жидкий эфир',\n 'canvas.backgroundFaultyTerminal': 'Сбойный терминал',\n 'canvas.backgroundSilk': 'Шёлк',\n 'canvas.backgroundGalaxy': 'Галактика',\n 'canvas.backgroundBlank': 'Пустой',\n 'canvas.backgroundUpload': 'Загрузить свой фон…',\n 'canvas.backgroundRemove': 'Убрать свой фон',\n 'canvas.templateLibrary': 'Библиотека шаблонов (выберите промпт — создадутся текстовый узел и узел конфигурации)',\n 'canvas.undo': 'Отменить (Ctrl+Z)',\n 'canvas.redo': 'Повторить (Ctrl+Shift+Z)',\n 'canvas.saving': 'Сохранение…',\n 'canvas.saved': 'Сохранено',\n 'canvas.saveFailed': 'Ошибка сохранения',\n 'canvas.loading': 'Загрузка…',\n 'canvas.toolSelect': 'Выбор / перемещение узлов',\n 'canvas.toolPan': 'Перетаскивание холста (Space / Ctrl)',\n 'canvas.delete': 'Удалить',\n 'canvas.duplicate': 'Дублировать (Ctrl+D)',\n 'canvas.download': 'Скачать изображение',\n 'canvas.fitView': 'Показать всё содержимое',\n 'canvas.minimapOpen': 'Открыть мини-карту',\n 'canvas.minimapClose': 'Скрыть мини-карту',\n 'canvas.minimap': 'Мини-карта холста',\n 'canvas.zoom': 'Масштаб',\n 'canvas.textPlaceholder': 'Введите текст…',\n 'canvas.generatingNode': 'Генерация…',\n 'canvas.generateFailed': 'Не удалось создать изображение',\n 'canvas.retry': 'Повторить',\n 'canvas.taskLost': 'Задача генерации потеряна (сервис перезапущен); повторите',\n 'canvas.emptyImageNode': 'Нажмите, чтобы загрузить изображение',\n 'canvas.connectHint': 'Потяните к другому узлу, чтобы соединить',\n 'canvas.connectAddHint': 'Потяните к другому узлу, чтобы соединить; клик добавляет следующий узел',\n 'canvas.resizeHint': 'Потяните, чтобы изменить размер (пропорции зафиксированы)',\n 'canvas.count': 'Кол-во изображений',\n 'canvas.configNode': 'Конфигурация генерации',\n 'canvas.addConfigNode': 'Добавить узел конфигурации',\n 'canvas.configHint': 'Подключите узлы изображений слева как референсы и текстовые узлы как промпт; выберите этот узел и генерируйте через панель ниже.',\n 'canvas.composerLinked': 'Подключено узлов ввода: {count}',\n 'canvas.composerPlaceholder': 'Опишите изображение; либо подключите текстовые узлы как промпт…',\n 'canvas.countUnit': '{count} шт.',\n 'canvas.model': 'Модель изображений',\n 'canvas.modelPlaceholder': 'Выберите модель',\n 'canvas.size': 'Пропорции',\n 'canvas.sizeAuto': 'Авто',\n 'canvas.quality': 'Качество',\n 'canvas.qualityAuto': 'Авто',\n 'canvas.needApi': 'Сначала настройте API генерации',\n 'canvas.needPrompt': 'Введите описание генерации',\n 'canvas.needModel': 'Нет доступной модели изображений',\n 'canvas.openSettings': 'Открыть настройки',\n 'canvas.deleteConnection': 'Удалить связь',\n 'canvas.paste': 'Вставить (Ctrl+V)',\n 'canvas.addImageNode': 'Добавить узел изображения',\n 'canvas.addTextNode': 'Добавить текстовый узел',\n 'canvas.addSketch': 'Добавить скетчборд (эскиз → изображение)',\n 'canvas.sketchNode': 'Скетчборд',\n 'canvas.addSketchNode': 'Добавить узел скетчборда',\n 'canvas.sketchPlaceholder': 'Нарисуйте эскиз от руки и подключите к узлу конфигурации как референс',\n 'canvas.sketchBrush': 'Кисть',\n 'canvas.sketchEraser': 'Ластик',\n 'canvas.sketchUndo': 'Отменить последний штрих',\n 'canvas.sketchClear': 'Очистить доску',\n 'canvas.sketchWidth': 'Толщина линии',\n 'canvas.dismiss': 'Закрыть',\n 'canvas.annotate': 'Разметка: обведите область, чтобы прикрепить карточку промпта',\n 'canvas.annotationNode': 'Промпт разметки',\n 'canvas.annotationTag': 'Разметка',\n 'canvas.annotationPlaceholder': 'Опишите, что должно быть в рамке, например: заменить на рыжего кота',\n 'canvas.annotationDrawHint': 'Проведите по изображению, чтобы нарисовать рамку; после отпускания появится карточка промпта, которая следует за изображением (соединять не нужно)',\n 'canvas.removeBackground': 'Удалить фон (локальная обработка, прозрачный PNG)',\n 'canvas.removingBackground': 'Удаляем фон…',\n 'canvas.removeBgDone': 'Фон удалён (прозрачных пикселей {percent}%)',\n 'canvas.removeBgFailed': 'Фон слишком сложный для локальной обработки; обведите объект разметкой и попросите модель перерисовать',\n 'canvas.alreadyTransparent': 'У этого изображения уже прозрачный фон',\n 'canvas.noBackgroundNode': 'Изображение без фона',\n 'canvas.splitLayers': 'Разделить слои (ИИ-разбор на редактируемые слои)',\n 'canvas.splittingLayers': 'Разделяем слои…',\n 'canvas.layerSplitDone': 'Разделено на {count} слоёв',\n 'canvas.layerSplitEmpty': 'Модель не нашла слоёв для разделения',\n 'canvas.layerBackground': 'Фон · {label}',\n 'canvas.layerObject': 'Слой · {label}',\n 'canvas.layerText': 'Текст · {label}',\n 'canvas.layerKindBackground': 'Фон',\n 'canvas.layerKindObject': 'Элемент',\n 'canvas.layerKindText': 'Текст',\n 'canvas.nodeModel': 'Модель для этого узла (повтор и подключённые узлы конфигурации)',\n 'canvas.modelDefault': 'Модель по умолчанию',\n 'canvas.fontSmaller': 'Меньше шрифт',\n 'canvas.fontLarger': 'Больше шрифт',\n 'canvas.bold': 'Жирный',\n 'canvas.textColor': 'Цвет текста',\n 'canvas.colorDefault': 'Как в теме',\n 'canvas.close': 'Закрыть',\n 'canvas.tabUpload': 'Загрузка',\n 'canvas.tabHistory': 'История',\n 'canvas.tabGallery': 'Материалы',\n 'canvas.imageMenuUpload': 'Локальное изображение',\n 'canvas.imageMenuAssets': 'Материалы',\n 'canvas.imageMenuHistory': 'История',\n 'canvas.imageMenuGenerate': 'AI-генерация',\n 'canvas.tabGenerate': 'AI-генерация',\n 'canvas.dropHint': 'Перетащите изображения сюда',\n 'canvas.dropSub': 'Несколько файлов, PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': 'Без названия',\n 'canvas.picked': 'Выбрано: {count}',\n 'canvas.generateAndAdd': 'Создать и добавить на холст',\n 'chat.expand': 'Показать диалог',\n 'chat.collapse': 'Скрыть диалог',\n 'chat.toggle': 'Диалог',\n 'config.resizeHint': 'Потяните, чтобы изменить ширину панели параметров',\n 'ecommerce.generation': 'Параметры генерации',\n 'ecommerce.title': 'Режим e-commerce',\n 'ecommerce.badge': 'Превью',\n 'ecommerce.short': 'E-com',\n 'ecommerce.anchorPending': 'Главное изображение готово, остальные генерируются по нему для единого стиля…',\n 'ecommerce.anchorFailed': 'Не удалось создать главное изображение, остальные приостановлены. Повторите главное (или всю группу) и продолжите.',\n 'ecommerce.anchorNote': 'Якорная генерация: сначала главное изображение, остальные создаются по нему для единого стиля',\n 'ecommerce.noAssetWarn': 'Товар не загружен: главное изображение будет создано по описанию и станет якорем; загрузите фото товара для большей точности',\n 'ecommerce.product': 'Товар',\n 'ecommerce.productName': 'Название товара (обязательно)',\n 'ecommerce.projectName': 'Название проекта (необязательно)',\n 'ecommerce.constraints': 'Преимущества и ограничения',\n 'ecommerce.sellingPoints': 'Доп. информация: бренд и модель, характеристики (объём/размер/вес), материалы, преимущества, сертификация, комплектация…',\n 'ecommerce.protectedFeatures': 'Что обязательно сохранить: цвет, логотип, текст упаковки, конструкцию',\n 'ecommerce.styleHint': 'Стиль, например: премиальный / реалистичный / естественный свет',\n 'ecommerce.setStructure': 'Структура комплекта',\n 'ecommerce.preview': 'Создать превью комплекта',\n 'ecommerce.planTitle': 'Будет сгенерировано изображений: {count}',\n 'ecommerce.planSlot': '{count} шт. · {description}',\n 'ecommerce.confirm': 'Сгенерировать весь комплект',\n 'ecommerce.uploadRef': 'Фото товара (обязательно, до 4 шт.: товар / упаковка / детали)',\n 'ecommerce.uploadShort': 'Загрузить материалы',\n 'ecommerce.sellingTitle': 'Преимущества товара',\n 'ecommerce.advanced': 'Дополнительно (площадка / ограничения)',\n 'ecommerce.params': 'Параметры',\n 'ecommerce.platformLabel': 'Площадка',\n 'ecommerce.languageLabel': 'Язык текста',\n 'ecommerce.customLanguageOption': 'Свой язык…',\n 'ecommerce.customLanguageLabel': 'Свой язык',\n 'ecommerce.customLanguagePlaceholder': 'Введите язык, например: Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': 'Пропорции',\n 'ecommerce.categoryLabel': 'Категория',\n 'ecommerce.multiSelect': 'Можно выбрать несколько',\n 'ecommerce.countHint': 'Нажмите, чтобы изменить количество (1-4)',\n 'ecommerce.refSettings': 'Настройки референсов (необязательно)',\n 'ecommerce.styleTitle': 'Стиль / дополнения',\n 'ecommerce.protectedLabel': 'Обязательно сохранить (необязательно)',\n 'ecommerce.assetsFull': 'Не более 4 фото товара',\n 'ecommerce.productRefTitle': 'Референсы товара',\n 'ecommerce.productRefHint': 'Обязательно · фото товара, до 4 шт.',\n 'ecommerce.styleRefTitle': 'Референс стиля',\n 'ecommerce.styleRefBadge': 'опционально, 1 шт.',\n 'ecommerce.styleRefHint': 'Только стиль, свет, композиция и настроение — сам товар заменить нельзя.',\n 'ecommerce.styleRefUpload': 'Загрузить референс стиля',\n 'ecommerce.refInfoTitle': 'Параметры товара',\n 'ecommerce.aiWrite': 'ИИ-заполнить',\n 'ecommerce.aiWriting': 'ИИ составляет…',\n 'ecommerce.aiWriteHint': 'Составить параметры товара из названия и категории',\n 'ecommerce.promptInfoPlaceholder': 'Опционально: бренд и модель, характеристики, материалы, преимущества, сертификация, комплектация… или ИИ-заполнить',\n 'ecommerce.optional': 'опционально',\n 'ecommerce.previewBoardTitle': 'Предпросмотр перед генерацией',\n 'ecommerce.previewBoardHint': 'Промпт каждого изображения можно изменить до запуска',\n 'ecommerce.promptReset': 'Сбросить',\n 'ecommerce.promptAutoHint': 'Автоматически · можно изменить',\n 'ecommerce.refSelect': 'Референс для этого назначения',\n 'ecommerce.refNone': 'Без референса',\n 'ecommerce.role.product': 'Товар',\n 'ecommerce.role.packaging': 'Упаковка',\n 'ecommerce.role.detail': 'Детали/ракурс',\n 'ecommerce.role.style': 'Стиль',\n 'ecommerce.footerReady': 'Будет сгенерировано {count} изображений',\n 'ecommerce.footerEmpty': 'Выберите структуру комплекта',\n 'ecommerce.results.title': 'Результаты комплекта',\n 'ecommerce.results.progress': '{done}/{total} готово',\n 'ecommerce.results.failed': '{count} с ошибкой',\n 'ecommerce.results.empty': 'После запуска генерации здесь появятся статус и результат каждого изображения',\n 'ecommerce.results.regenerate': 'Сгенерировать заново',\n 'ecommerce.results.export': 'Экспорт списка',\n 'ecommerce.results.newProduct': 'Новый товар',\n 'prompt.placeholder': 'Опишите желаемое изображение, например: рыжий кот в шлеме космонавта смотрит в телескоп на Луне, акварель, мягкий свет…',\n 'prompt.required': 'Введите промпт',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': 'Превышен лимит промпта для этой модели ({limit} символов); сократите текст',\n 'prompt.enhance': 'Улучш.',\n 'prompt.enhancing': 'Улучшаем…',\n 'prompt.enhanceHint': 'Развернуть краткий промпт через настроенную диалоговую модель',\n 'prompt.configTitle': 'Сначала настройте модель улучшения промптов',\n 'prompt.configHint': 'Настройки открыты. Перейдите в «Плагины → ИИ-генерация», укажите или переиспользуйте API-адрес и ключ, выберите диалоговую модель и сохраните.',\n 'tasks.title': 'Задачи генерации',\n 'tasks.queued': 'В очереди',\n 'tasks.running': 'Генерация',\n 'tasks.completed': 'Готово',\n 'tasks.failed': 'Ошибка',\n 'tasks.cancelled': 'Отменено',\n 'tasks.cancel': 'Отменить',\n 'tasks.retry': 'Повторить',\n 'params.size': 'Размер',\n 'params.quality': 'Чёткость',\n 'params.count': 'Количество',\n 'params.detail': 'Детализация',\n 'size.auto': 'Авто',\n 'size.square': '1:1 квадрат',\n 'size.portrait34': '3:4 верт.',\n 'size.landscape43': '4:3 гориз.',\n 'size.portrait916': '9:16 верт.',\n 'size.portrait23': '2:3 верт.',\n 'size.landscape32': '3:2 гориз.',\n 'size.wide169': '16:9 шир.',\n 'size.ultrawide21': '21:9 ультрашир.',\n 'quality.auto': 'Авто',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1 шт.',\n 'count.two': '2 шт.',\n 'count.three': '3 шт.',\n 'count.four': '4 шт.',\n 'detail.auto': 'Авто',\n 'detail.standard': 'Стандарт',\n 'detail.high': 'Высокая',\n 'detail.hint': 'Сквозной параметр, поддерживают некоторые шлюзы gpt-image-2; для официального API оставьте «Авто»',\n 'model.label': 'Модель',\n 'model.channel': 'Канал',\n 'model.channelPlaceholder': 'Канал по умолчанию',\n 'templates.favoritesCount': 'В избранном: {count}',\n 'model.noEditModels': 'В текущем режиме нет доступных моделей',\n 'compare.enable': 'Сравнение моделей',\n 'compare.models': 'Модели для сравнения',\n 'compare.title': 'Сравнение результатов моделей',\n 'compare.fullscreen': 'Полноэкранное сравнение',\n 'compare.selectRequired': 'Выберите хотя бы одну модель для сравнения',\n 'generate': 'Сгенерировать',\n 'generating': 'Генерация…',\n 'edit.upload': 'Нажмите или перетащите референс',\n 'edit.uploadHint': 'PNG / JPG / WEBP, до 10 МБ',\n 'edit.change': 'Заменить изображение',\n 'edit.remove': 'Убрать',\n 'edit.required': 'Сначала загрузите референс',\n 'canvas.emptyTitle': 'Начните творить',\n 'canvas.emptyHint': 'Двойной клик или нижняя панель добавляют узлы; соедините узлы изображений и текста с целевым узлом, выберите его и генерируйте внизу',\n 'canvas.new': 'Новая генерация',\n 'canvas.newHint': 'Начать новую генерацию и очистить просмотр',\n 'canvas.error': 'Ошибка генерации: {error}',\n 'canvas.submitting': 'Ставим в очередь…',\n 'canvas.queued': 'Задача в очереди',\n 'canvas.queueHint': 'Сейчас ожидают {count} задач',\n 'canvas.generating': 'Генерируем изображение…',\n 'canvas.elapsed': 'Прошло {seconds} с',\n 'canvas.images': 'Сгенерировано: {count}',\n 'download': 'Скачать',\n 'revisedPrompt': 'Улучшенный промпт: {prompt}',\n 'history.title': 'История',\n 'history.empty': 'Истории пока нет — созданные изображения появятся здесь',\n 'history.clear': 'Очистить',\n 'history.clearConfirm': 'Очистить всю историю? Действие необратимо.',\n 'history.restore': 'Восстановить',\n 'history.delete': 'Удалить',\n 'history.images': 'шт.',\n 'history.viewing': 'История · {time}',\n 'history.search': 'Поиск по промптам и моделям…',\n 'history.model': 'Фильтр по модели',\n 'history.ratio': 'Фильтр по пропорциям',\n 'history.allModels': 'Все модели',\n 'history.allRatios': 'Все пропорции',\n 'gallery.title': 'Материалы',\n 'gallery.categories': 'Категории',\n 'gallery.all': 'Все материалы',\n 'gallery.upload': 'Загрузить',\n 'gallery.uploadModel': 'Локальный файл',\n 'gallery.uploaded': 'Материал добавлен',\n 'gallery.uploadHint': 'Сохраните локальные изображения для холста',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': 'Пропорции',\n 'gallery.tags': 'Метки',\n 'gallery.filterHint': 'Фильтр галереи по режиму, модели и пропорциям',\n 'gallery.count': '· всего {count}',\n 'gallery.viewMode': 'Вид',\n 'gallery.masonry': 'Плитка',\n 'gallery.grid': 'Сетка',\n 'gallery.sort': 'Сортировка',\n 'gallery.newest': 'Сначала новые',\n 'gallery.oldest': 'Сначала старые',\n 'gallery.untitled': 'Без названия',\n 'gallery.search': 'Поиск по работам и моделям…',\n 'gallery.tagsPlaceholder': 'Метки через запятую',\n 'gallery.tagsApply': 'Добавить метки',\n 'gallery.editTags': 'Править метки',\n 'gallery.tagsEditShort': 'Правка',\n 'gallery.tagsSave': 'Сохранить',\n 'gallery.tagsCancel': 'Отмена',\n 'gallery.selected': 'Выбрано: {count}',\n 'gallery.selectionDone': 'Завершить выбор',\n 'gallery.selectionClear': 'Снять выбор',\n 'gallery.downloadSelected': 'Скачать выбранные',\n 'gallery.exportJson': 'Экспорт JSON',\n 'gallery.select': 'Выбрать работы',\n 'gallery.add': 'В галерею',\n 'gallery.added': 'Добавлено в галерею',\n 'gallery.already': 'Уже в галерее',\n 'gallery.delete': 'Убрать из галереи',\n 'gallery.openFolder': 'Открыть папку',\n 'gallery.clear': 'Очистить галерею',\n 'gallery.openFolderHint': 'Открыть каталог сохранения изображений (~/.dsh/dsh-imagegen) в проводнике',\n 'gallery.clearConfirm': 'Очистить всю галерею? Действие необратимо.',\n 'gallery.empty': 'Галерея пуста — добавьте понравившиеся изображения',\n 'gallery.viewing': 'Галерея · {time}',\n 'preview.title': 'Просмотр изображения',\n 'preview.open': 'Открыть просмотр',\n 'preview.close': 'Закрыть',\n 'preview.prev': 'Назад',\n 'preview.next': 'Вперёд',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': 'Управление масштабом',\n 'preview.zoomIn': 'Увеличить',\n 'preview.zoomOut': 'Уменьшить',\n 'preview.zoomReset': 'Сбросить масштаб',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': 'Копировать промпт',\n 'preview.copied': 'Скопировано',\n 'preview.addToEdit': 'В режим img2img',\n 'config.missing': 'API не настроен: откройте «Настройки → Плагины → Настраиваемые» и заполните api_url и api_key для ИИ-генерации.',\n 'config.generationTitle': 'Сначала настройте API генерации',\n 'config.generationHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Укажите адрес и ключ API генерации, сохраните — и можно творить.',\n 'config.enhancementTitle': 'Сначала настройте модель улучшения промптов',\n 'config.enhancementHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Укажите или переиспользуйте адрес и ключ диалогового API, выберите модель и сохраните.',\n 'config.disabledTitle': 'Плагин ИИ-генерации отключён',\n 'config.disabledHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Включите плагин — и генерация заработает.',\n 'config.configured': 'Подключено: {url}',\n 'config.disabled': 'Плагин отключён — включите его в настройках.',\n 'connection.connected': 'Подключено',\n 'connection.disconnected': 'Нет подключения',\n 'panel.collapseConfig': 'Свернуть параметры',\n 'panel.expandConfig': 'Развернуть параметры',\n 'update.available': 'Доступна новая версия: {version}',\n 'update.install': 'Обновить онлайн',\n 'update.installing': 'Обновляем…',\n 'update.success': 'Обновлено до {version}, перезапустите DSH',\n 'update.failed': 'Не удалось обновить, попробуйте ещё раз',\n 'update.release': 'Открыть релиз',\n 'settings.title': 'ИИ-генерация (dsh-imagegen)',\n 'settings.description': 'Адрес и ключ API генерации изображений',\n 'settings.summary': 'Настройка каналов генерации изображений, моделей и переключателей плагина',\n 'settings.currentVersion': 'Текущая версия',\n 'settings.apiUrl': 'Адрес API (api_url)',\n 'settings.apiUrlHint': 'База OpenAI-совместимого API, например https://api.openai.com/v1; /images/generations и /images/edits дописываются автоматически',\n 'settings.apiKey': 'Ключ API (api_key)',\n 'settings.apiKeyHint': 'Bearer-ключ хранится открыто в локальном документе настроек; интерфейс показывает лишь факт его наличия',\n 'settings.apiKeySet': 'Ключ сохранён; введите новый, чтобы заменить, или нажмите «Очистить»',\n 'settings.apiKeyClear': 'Очистить',\n 'settings.imageModelsTitle': 'Модели генерации',\n 'settings.imageModelsHint': 'После сохранения адреса и ключа будут найдены кандидаты; выбирайте только те, что реально умеют генерировать изображения.',\n 'settings.imageModels': 'Разрешённые модели генерации',\n 'settings.imageModelsManualHint': 'По одной модели в строке; можно заполнить вручную, если API не отдаёт /models. Панель и агент используют только этот список.',\n 'settings.imageModelsFetch': 'Найти модели',\n 'settings.imageModelsLoading': 'Ищем…',\n 'settings.imageModelsCandidates': 'Найденные кандидаты (отметьте и сохраните)',\n 'settings.addModel': '+ Добавить вручную',\n 'settings.cancelAddModel': 'Скрыть добавление',\n 'settings.addModelPlaceholder': 'Имя модели, например qwen-image',\n 'settings.addModelConfirm': 'Добавить',\n 'settings.removeModel': 'Удалить модель',\n 'settings.optional': 'необязательно',\n 'settings.moreOptions': 'Дополнительно',\n 'settings.promptEnhanceTitle': 'Модель улучшения промптов',\n 'settings.promptEnhanceHint': 'Разворачивает краткое описание в полноценный промпт; пустые адрес и ключ наследуют настройки генерации.',\n 'settings.promptApiUrl': 'Адрес диалогового API (необязательно)',\n 'settings.promptApiUrlHint': 'OpenAI-совместимая база; если пусто — используется адрес API генерации.',\n 'settings.promptApiKey': 'Ключ диалогового API (необязательно)',\n 'settings.promptApiKeyHint': 'Если пусто — используется ключ API генерации.',\n 'settings.promptModel': 'Диалоговая модель',\n 'settings.promptModelHint': 'Выберите или впишите модель с поддержкой /chat/completions.',\n 'settings.promptModelDetectionHint': 'По умолчанию используется API генерации; после поиска просто выберите модель.',\n 'settings.promptModelsFetch': 'Получить модели',\n 'settings.promptModelsLoading': 'Получаем…',\n 'settings.promptModelsSelect': 'Выберите модель',\n 'settings.promptModelsCandidates': 'Найденные диалоговые модели',\n 'settings.addPromptModelPlaceholder': 'Имя диалоговой модели, например gpt-4.1-mini',\n 'settings.promptApiAdvanced': 'Отдельный диалоговый API (необязательно)',\n 'settings.announceToAgent': 'Оповещать агентов о плагине',\n 'settings.announceToAgentHint': 'Включено — возможности плагина добавляются в системный промпт каждого агента',\n 'settings.allowAgentImageGeneration': 'Разрешить агентам генерацию',\n 'settings.allowAgentImageGenerationHint': 'Включено по умолчанию. Выключено — агенты не могут создавать, смотреть или отменять задачи; боковая панель работает.',\n 'settings.enabled': 'Включить плагин',\n 'settings.enabledHint': 'Выключено — панель генерации недоступна (карточка настроек работает всегда)',\n 'settings.save': 'Сохранить',\n 'settings.saving': 'Сохраняем…',\n 'settings.discard': 'Отменить правки',\n 'settings.unsaved': 'Есть несохранённые правки',\n 'settings.saveFailed': 'Не удалось сохранить, попробуйте ещё раз',\n 'settings.readOnly': 'Документ настроек сейчас только для чтения, сохранить нельзя.',\n 'settings.notExposed': 'Пространство настроек недоступно: этот деплой не предоставляет сервис настроек плагина.',\n 'settings.expand': 'Развернуть',\n 'settings.collapse': 'Свернуть',\n 'settings.inherit': 'Наследовать',\n 'settings.on': 'Вкл',\n 'settings.off': 'Выкл',\n 'settings.overridden': 'Переопределено',\n 'settings.reset': 'Сбросить',\n 'settings.invalidNumber': 'Введите корректное число',\n\n\n\n 'templates.open': 'Шаблоны',\n 'templates.title': 'Библиотека промпт-шаблонов',\n 'templates.sources': 'Источники шаблонов',\n 'templates.meta': 'Всего шаблонов: {count} · {origin}',\n 'templates.origin.bundled': 'встроенный снимок',\n 'templates.origin.refreshed': 'обновлено онлайн',\n 'templates.search': 'Поиск по названиям и промптам…',\n 'templates.all': 'Все',\n 'templates.close': 'Закрыть',\n 'templates.back': 'К списку',\n 'templates.use': 'Использовать промпт',\n 'templates.copy': 'Копировать промпт',\n 'templates.copied': 'Скопировано',\n 'templates.refresh': 'Обновить библиотеку',\n 'templates.refreshing': 'Обновляем…',\n 'templates.refreshed': 'Обновлено, шаблонов: {count}',\n 'templates.refreshFailed': 'Ошибка обновления: {error}',\n 'templates.favorites': 'Избранное',\n 'templates.favoritesHint': 'Показать только избранные шаблоны',\n 'templates.favoritesEmpty': 'Пока пусто — нажмите звёздочку в углу карточки, чтобы сохранить шаблон',\n 'templates.favoriteAdd': 'Добавить в избранное',\n 'templates.favoriteRemove': 'Убрать из избранного',\n 'templates.favorite': 'В избранное',\n 'templates.unfavorite': 'В избранном',\n 'templates.cacheAll': 'Кэшировать все картинки',\n 'templates.cacheAllHint': 'Через локальный прокси сохранит все референсы этой библиотеки на диск — дальше доступно офлайн',\n 'templates.caching': 'Кэшируем {done}/{total}…',\n 'templates.cached': 'Все картинки закэшированы',\n 'templates.empty': 'Нет подходящих шаблонов',\n 'templates.loading': 'Загружаем библиотеку…',\n 'templates.loadFailed': 'Не удалось загрузить библиотеку: {error}',\n 'templates.retry': 'Повторить',\n 'templates.attribution': 'Шаблоны и изображения принадлежат сайтам-источникам; ссылки на авторов — в карточках',\n 'templates.source': 'Источник: {label}',\n 'templates.featured': 'Выбор редакции',\n 'inspiration.title': 'Вдохновение',\n 'inspiration.shuffle': 'Другие',\n 'inspiration.shuffling': 'Подбираем…',\n 'inspiration.useHint': 'Нажмите, чтобы использовать промпт',\n 'channels.title': 'Каналы',\n 'channels.hint': 'Укажите адрес и ключ API каждого канала — и модели станут доступны',\n 'channels.empty': 'Каналов пока нет — добавьте кнопкой ниже',\n 'channels.addProvider': 'Добавить провайдера',\n 'channels.addCustom': 'Добавить свой канал',\n 'channels.untitled': 'Канал без названия',\n 'channels.keySet': 'Ключ задан',\n 'channels.keyMissing': 'Ключ не задан',\n 'channels.modelCount': 'Моделей: {n}',\n 'channels.noModels': 'Модели не настроены',\n 'channels.defaultLabel': 'По умолчанию',\n 'channels.statusReady': 'Готов',\n 'channels.statusIncomplete': 'Настройка не завершена',\n 'channels.edit': 'Править',\n 'channels.delete': 'Удалить',\n 'channels.deleteConfirmTitle': 'Удалить канал «{name}»? Ключ канала будет удалён; история и галерея сохранятся.',\n 'channels.confirm': 'Удалить',\n 'channels.cancel': 'Отмена',\n 'channels.editorTitle': 'Канал',\n 'channels.editorSaveNote': 'Изменения вступят в силу после «Сохранить» внизу карточки',\n 'channels.displayName': 'Название',\n 'channels.apiUrl': 'Адрес API',\n 'channels.apiKey': 'Ключ API',\n 'channels.keyReplaceHint': 'Задан — введите новый, чтобы заменить',\n 'channels.keyMissingHint': 'Введите ключ',\n 'channels.keyClear': 'Очистить ключ',\n 'channels.modelCatalogTitle': 'Каталог моделей',\n 'channels.noModelsHint': 'Моделей пока нет: нажмите «Найти» для списка кандидатов или добавьте вручную; псевдоним по умолчанию равен id модели и его можно менять.',\n 'channels.detect': 'Найти снова',\n 'channels.detecting': 'Ищем…',\n 'channels.detectSuccess': 'Связь есть ✓ · кандидатов: {n}',\n 'channels.detectFailed': 'Ошибка поиска: {error}',\n 'channels.detectOk': 'API отвечает',\n 'channels.modelAliasLabel': 'Псевдоним (отображается)',\n 'channels.modelIdLabel': 'id модели у провайдера',\n 'channels.unknownProtocol': 'Протокол неизвестен — пробуем универсальный OpenAI',\n 'channels.removeModel': 'Удалить модель',\n 'channels.manualAddPlaceholder': 'id модели, например qwen-image',\n 'channels.addModelConfirm': 'Добавить',\n 'channels.copyFrom': 'Скопировать из канала…',\n 'channels.copyApply': 'Копировать',\n 'channels.candidatesTitle': 'Кандидаты (нажмите, чтобы добавить)',\n 'channels.deleteThisChannel': 'Удалить этот канал',\n 'channels.setDefault': 'Сделать каналом по умолчанию',\n 'channels.presetPickerTitle': 'Добавить провайдера',\n 'channels.presetPickerHint': 'Выберите встроенного провайдера — останется ввести только ключ API',\n 'channels.presetCustomHint': 'Свой адрес, ключ и каталог моделей',\n 'channels.presetLoadFailed': 'Не удалось загрузить провайдеров: {error}',\n 'canvas.skills.fileNode': 'Узел файла',\n 'canvas.skills.addFileNode': 'Добавить узел файла',\n 'canvas.skills.emptyFileNode': 'Нажмите, чтобы загрузить файл',\n 'canvas.skills.fileMenuUpload': 'Загрузить файл',\n 'canvas.skills.fileDropHint': 'Перетащите файл сюда для загрузки',\n 'canvas.skills.fileDropSub': 'PDF / Office / текст / изображения, до 50 МБ',\n 'canvas.skills.fileSizeUnknown': 'Размер неизвестен',\n 'canvas.skills.filePreviewUnavailable': 'Для этого формата нет предпросмотра — скачайте файл или запустите навык',\n 'canvas.skills.filePreviewLoading': 'Загружаем предпросмотр…',\n 'canvas.skills.fileDownload': 'Скачать файл',\n 'canvas.skills.fileOpen': 'Открыть системным приложением',\n 'canvas.skills.fileKind.text': 'Текст',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': 'Изображение',\n 'canvas.skills.fileKind.office': 'Документ',\n 'canvas.skills.fileKind.archive': 'Архив',\n 'canvas.skills.fileKind.audio': 'Аудио',\n 'canvas.skills.fileKind.video': 'Видео',\n 'canvas.skills.fileKind.other': 'Файл',\n 'canvas.skills.fileTooLarge': 'Файл превышает лимит {size} МБ',\n 'canvas.skills.fileTypeBlocked': 'Файлы {ext} не загружаются из соображений безопасности',\n 'canvas.skills.fileUploadFailed': 'Не удалось загрузить: {message}',\n 'canvas.preview.expand': 'Открыть предпросмотр',\n 'canvas.preview.close': 'Закрыть предпросмотр',\n 'canvas.preview.copy': 'Скопировать содержимое',\n 'canvas.preview.copied': 'Скопировано',\n 'canvas.preview.openTab': 'Открыть в новой вкладке',\n 'canvas.preview.retry': 'Повторить',\n 'canvas.preview.failed': 'Не удалось прочитать предпросмотр: {message}',\n 'canvas.preview.empty': 'Файл пуст',\n 'canvas.preview.textTruncated': 'Большой файл — показаны первые {chars} символов',\n 'canvas.preview.tableTruncated': 'Большая таблица — показаны первые {rows} строк',\n 'canvas.preview.archiveTruncated': 'Много записей — показаны первые {count}',\n 'canvas.preview.docTruncated': 'Длинный документ — показаны первые {blocks} блоков',\n 'canvas.preview.slidesTruncated': 'Большая презентация — показаны первые {count} слайдов',\n 'canvas.preview.slideOf': 'Слайд {page}',\n 'canvas.preview.rendered': 'Рендер',\n 'canvas.preview.source': 'Исходник',\n 'canvas.preview.pdfFallback': 'В этой среде PDF не отображается встроенно — скачайте файл',\n 'canvas.preview.pdfHeuristic': 'Текст PDF извлекается эвристически; сложная вёрстка может потерять порядок',\n 'canvas.preview.reason.unsupported': 'В этом формате нет читаемого содержимого — скачайте файл или превратите его в узел изображения или текста для навыка',\n 'canvas.preview.reason.unreadable': 'Из файла не удалось извлечь текст (скан или необычная кодировка); преобразуйте его в узел изображения для визуальной модели',\n 'canvas.preview.doubleClick': 'Двойной щелчок открывает предпросмотр',\n 'canvas.skills.button': 'Навык',\n 'canvas.skills.menuTitle': 'Запустить навык',\n 'canvas.skills.runOn': 'Запустить навык на «{name}»',\n 'canvas.skills.pickerTitle': 'Выберите навык',\n 'canvas.skills.searchPlaceholder': 'Поиск навыков…',\n 'canvas.skills.groupBuiltin': 'Встроенные действия',\n 'canvas.skills.groupExternal': 'Локальные навыки',\n 'canvas.skills.emptyExternal': 'Локальных навыков нет — установите их в ~/.dsh/skills и обновите',\n 'canvas.skills.empty': 'Навыки недоступны',\n 'canvas.skills.loading': 'Загружаем список навыков…',\n 'canvas.skills.tierLight': 'Быстро',\n 'canvas.skills.tierHeavy': 'Долгая задача',\n 'canvas.skills.accepts': 'Работает с: {types}',\n 'canvas.skills.incompatible': 'Навык не применим к этому типу узла',\n 'canvas.skills.disabled': 'Навыки холста отключены в настройках плагина',\n 'canvas.skills.noRegistry': 'В этом хосте нет реестра навыков (ctx.skills) — доступны только встроенные действия',\n 'canvas.skills.registryFailed': 'Не удалось получить список навыков: {message}',\n 'canvas.skills.needAgent': 'Нужен агентный рантайм хоста, которого нет в этой сборке DSH — долгие задачи недоступны',\n 'canvas.skills.unknown': 'Такого навыка больше нет',\n 'canvas.skills.canvasMissing': 'Холст не найден — обновите страницу и повторите',\n 'canvas.skills.noInput': 'У этого узла нет данных для обработки',\n 'canvas.skills.noChat': 'Быстрым навыкам нужна чат-модель: Настройки → Плагины → ИИ-генерация → Улучшение промпта',\n 'canvas.skills.noDescription': '(описание не указано)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': 'Вы выполняете навык «{name}». Строго следуйте его инструкциям для входа ниже, отвечайте на языке входа и выводите только результат — не пересказывайте инструкции.',\n 'canvas.skills.externalHeader': 'Примените навык «{name}» к данным ниже.',\n 'canvas.skills.externalHeavyCost': 'Это долгая задача: запускается безголовый агент и может израсходовать много квоты модели.',\n 'canvas.skills.extraInstruction': 'Дополнительное требование:',\n 'canvas.skills.inputsHeader': 'Входные данные:',\n 'canvas.skills.inputsEmpty': '(читаемого текста нет — работайте по приложенным изображениям)',\n 'canvas.skills.needInstruction': 'Введите своё указание для обработки',\n 'canvas.skills.needImage': 'Этому навыку нужен узел изображения',\n 'canvas.skills.needText': 'Этому навыку нужен текстовый узел с содержимым',\n 'canvas.skills.fileMissing': 'Файловый ресурс отсутствует или удалён',\n 'canvas.skills.extractFailed': 'Из этого файла не удалось извлечь содержимое',\n 'canvas.skills.nothingToRead': 'Читаемого содержимого не найдено',\n 'canvas.skills.truncated': 'Содержимое обрезано до {count} символов',\n 'canvas.skills.tooManyImages': 'Изображений больше {count} — обработаны только первые',\n 'canvas.skills.extractNodeTitle': 'Содержимое «{name}»',\n 'canvas.skills.summaryHead': 'Собрано узлов-источников: {count}',\n 'canvas.skills.describeAsk': 'Опишите эти изображения ({count}).',\n 'canvas.skills.polishNodeTitle': 'Результат обработки ({style})',\n 'canvas.skills.skillMissing': 'Навык «{name}» не установлен. Сначала установите его (например, попросите агента DSH установить https://github.com/ningzimu/image-to-editable-ppt-skill) и повторите.',\n 'canvas.skills.agentFailed': 'Не удалось создать безголового агента — проверьте модель и пресет агента',\n 'canvas.skills.needModel': 'Хост не публикует модель по умолчанию (agent-default-model), поэтому долгая задача не может запустить агента. Выберите модель по умолчанию в «Настройки → Общие» и повторите.',\n 'canvas.skills.noOutputFile': 'Навык не создал файл; ответ агента сохранён текстовым узлом',\n 'canvas.skills.heavyIntro': 'Вы выполняете навык «{name}» для визуального холста. Пройдите всю процедуру строго по документации, не пропуская шагов.',\n 'canvas.skills.heavyBodyHeader': 'Инструкции навыка:',\n 'canvas.skills.heavyInputsHeader': 'Входные файлы задачи (абсолютные пути):',\n 'canvas.skills.heavyOutputHeader': 'Запишите итоговый артефакт в этот каталог: {dir}',\n 'canvas.skills.heavyRules': 'Требования:\\n1. Все промежуточные файлы держите в рабочем каталоге; итоговый артефакт — только в каталоге выше.\\n2. По завершении коротко ответьте: имена файлов, успех, замечания.',\n 'canvas.skills.heavyAsk': 'Выполните навык «{name}».',\n 'canvas.skills.heavyAskInputs': 'Каталог входных файлов: {dir}',\n 'canvas.skills.heavyAskOutput': 'Каталог вывода: {dir}',\n 'canvas.skills.heavyAskLoadSkill': '(Если инструкций навыка нет в контексте, сначала загрузите их по имени «{name}».)',\n 'canvas.skills.stageQueued': 'В очереди',\n 'canvas.skills.stagePreparing': 'Готовим входные файлы',\n 'canvas.skills.stageReading': 'Читаем содержимое узлов',\n 'canvas.skills.stageRunning': 'Навык выполняется',\n 'canvas.skills.stageCollecting': 'Собираем артефакты',\n 'canvas.skills.stageDone': 'Готово',\n 'canvas.skills.stageCancelled': 'Отменено',\n 'canvas.skills.stagePrepared': 'Подготовлено входных файлов: {count}',\n 'canvas.skills.stageArtifacts': 'Выполняется — создано файлов: {count}',\n 'canvas.skills.runElapsed': 'Прошло',\n 'canvas.skills.runRetry': 'Повторить',\n 'canvas.skills.runStarted': 'Навык «{name}» запущен',\n 'canvas.skills.runDone': 'Навык завершён — новых узлов: {count}',\n 'canvas.skills.runFailed': 'Навык завершился ошибкой: {message}',\n 'canvas.skills.runCancelled': 'Запуск навыка отменён',\n 'canvas.skills.cancel': 'Отменить запуск',\n 'canvas.skills.cancelling': 'Отменяем…',\n 'canvas.skills.confirmTitle': 'Запустить долгую задачу',\n 'canvas.skills.confirmBody': '«{name}» запускает безголового агента на этой машине. Это может занять от нескольких до десятков минут и израсходовать много квоты модели.',\n 'canvas.skills.confirmCost': 'Стоимость: {hint}',\n 'canvas.skills.confirmRequires': 'Требуется: {list}',\n 'canvas.skills.confirmRun': 'Запустить',\n 'canvas.skills.confirmCancel': 'Не сейчас',\n 'canvas.skills.batchTitle': 'Пакетный запуск',\n 'canvas.skills.batchNote': 'Запуск по одному узлу подряд (узлов: {count})',\n 'canvas.skills.batchTooMany': 'Не более {count} узлов за запуск',\n 'canvas.skills.sourceBadge': 'Результат навыка',\n 'canvas.skills.sourceTip': 'Создано навыком «{name}» из узлов-источников: {count}',\n 'canvas.skills.rerun': 'Запустить тот же навык снова',\n 'canvas.skills.history': 'История запусков',\n 'canvas.skills.historyEmpty': 'Запусков пока нет',\n 'canvas.skills.needConfirm': 'Этот навык требует подтверждения',\n 'canvas.skills.polish': 'Обработка текста',\n 'canvas.skills.polishDesc': 'Переписать текстовый узел в выбранном стиле: формальнее / разговорнее / короче / подробнее или своё указание',\n 'canvas.skills.describe': 'Описание изображения',\n 'canvas.skills.describeDesc': 'Прочитать изображение визуальной моделью и создать текстовый узел (годится как промпт)',\n 'canvas.skills.extract': 'Извлечь содержимое',\n 'canvas.skills.extractDesc': 'Извлечь текст из файла или изображения в текстовый узел (текст / код / OOXML / PDF)',\n 'canvas.skills.ppt': 'Изображение → редактируемый PPT',\n 'canvas.skills.pptDesc': 'Запускает локальный навык image-to-editable-ppt и создаёт редактируемый .pptx как узел файла',\n 'canvas.skills.pptCost': 'Долгая задача: презентация на 10 страниц может израсходовать много квоты; одна страница часто занимает 10+ минут',\n 'canvas.skills.pptRequires': 'Нужен установленный навык image-to-editable-ppt, а также токен OCR и образный бэкенд по его документации',\n 'canvas.polish.button': 'ИИ-обработка',\n 'canvas.polish.title': 'ИИ-обработка текста',\n 'canvas.polish.formal': 'Формальнее',\n 'canvas.polish.casual': 'Разговорнее',\n 'canvas.polish.shorter': 'Короче',\n 'canvas.polish.expand': 'Подробнее',\n 'canvas.polish.custom': 'Своё',\n 'canvas.polish.customPlaceholder': 'напр. переписать как пост для соцсети с эмодзи и хэштегами',\n 'canvas.polish.run': 'Обработать',\n 'canvas.polish.previewTitle': 'Было / стало',\n 'canvas.polish.apply': 'Применить',\n 'canvas.polish.revert': 'Вернуть',\n 'canvas.polish.applied': 'Результат применён',\n 'canvas.polish.directHint': 'Текущая настройка: результат сразу заменяет текст (в настройках можно включить предпросмотр)',\n 'settings.skillsEnabled': 'Навыки холста',\n 'canvas.skills.libraryButton': 'Библиотека навыков',\n 'canvas.skills.libraryTitle': 'Библиотека навыков',\n 'canvas.skills.libraryInstalled': 'Установленные навыки ({count})',\n 'canvas.skills.libraryEmpty': 'Навыков пока нет. Установите по ссылке на репозиторий или загрузите архив.',\n 'canvas.skills.libraryRoot': 'Каталог установки: {root}',\n 'canvas.skills.libraryInstall': 'Установка по ссылке',\n 'canvas.skills.libraryInstallHint': 'Репозиторий или папка GitHub, отдельный SKILL.md, любой git-адрес или ссылка на zip; несколько ссылок — с новой строки. Хост подхватывает установку без перезапуска.',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo или …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': 'Загрузить архив',\n 'canvas.skills.libraryUploadHint': 'Загрузите zip с файлом SKILL.md (≤50 МБ): он распакуется в ~/.dsh/skills.',\n 'canvas.skills.libraryDropzone': 'Нажмите или перетащите zip-архив сюда',\n 'canvas.skills.libraryForce': 'Заменить навык с тем же именем',\n 'canvas.skills.libraryInstallAction': 'Установить',\n 'canvas.skills.libraryInstalling': 'Установка…',\n 'canvas.skills.libraryRemove': 'Удалить',\n 'canvas.skills.libraryRemoving': 'Удаление…',\n 'canvas.skills.libraryRemoveConfirm': 'Удалить навык «{name}»? Его папка будет стёрта.',\n 'canvas.skills.libraryInstalledToast': 'Установлено: {names}',\n 'canvas.skills.libraryFailedToast': 'Ошибка установки: {message}',\n 'canvas.skills.libraryRemovedToast': 'Удалено: {name}',\n 'canvas.skills.libraryUnavailable': 'Этот хост не управляет библиотекой навыков',\n 'canvas.skills.libraryReload': 'Обновить',\n 'canvas.skills.libraryOpenFolder': 'Открыть каталог',\n 'canvas.skills.installNow': 'Установить',\n 'canvas.skills.needsInstall': 'Не установлен',\n 'canvas.skills.missingHint': 'Навык «{name}» ещё не установлен — установите его, чтобы использовать',\n 'canvas.skills.installArchiveFailed': 'Ошибка установки архива: {message}',\n 'canvas.skills.libraryIssueFrontmatter': 'Нет YAML frontmatter (нужны name и description), поэтому реестр навыков хоста его не загружает и узлы не могут его запустить',\n 'canvas.skills.libraryIssueName': 'Имя в frontmatter «{name}» не в kebab-case (строчные буквы, цифры и дефисы), поэтому реестр навыков хоста его игнорирует и узлы не могут его запустить',\n 'canvas.skills.libraryIssueDescription': 'В frontmatter файла SKILL.md нет description (обязательно), поэтому реестр навыков хоста его не загружает и узлы не могут его запустить',\n 'canvas.skills.libraryIssueInvocation': 'Запись disable-model-invocation / user-invocable не разбирается, поэтому реестр навыков хоста его игнорирует и узлы не могут его запустить',\n 'canvas.skills.libraryIssueUserOnly': 'Навык объявляет disable-model-invocation: он доступен только человеку, поэтому узлы холста не могут его запустить',\n 'canvas.skills.configOpen': 'Настроить',\n 'canvas.skills.configSave': 'Сохранить',\n 'canvas.skills.configSaveApply': 'Сохранить и применить',\n 'canvas.skills.configSaving': 'Сохранение…',\n 'canvas.skills.configApplying': 'Применение…',\n 'canvas.skills.configSaved': 'Сохранено',\n 'canvas.skills.configSaveFailed': 'Не удалось сохранить',\n 'canvas.skills.configApplied': 'Сохранено и применено',\n 'canvas.skills.configApplyFailed': 'Не удалось применить',\n 'canvas.skills.configClear': 'Очистить',\n 'canvas.skills.configSecretSet': 'Задан (пустое поле оставит как есть)',\n 'canvas.skills.configSecretUnset': 'Не задан',\n 'canvas.skills.configMissingHint': 'Требуется настройка: {fields}',\n 'canvas.skills.configGo': 'Настроить',\n 'canvas.skills.configSourceSkill': 'Поля объявлены собственным skill.config.json навыка',\n 'canvas.skills.configSourcePlugin': 'Поля взяты из встроенного рецепта плагина для известного навыка (собственное объявление навыка важнее)',\n 'canvas.skills.configHeader': 'Уже настроено:',\n 'canvas.skills.configUnwritable': 'Этот хост не может записывать настройки, поэтому настройка навыков недоступна',\n 'canvas.skills.configIssueVersion': 'В skill.config.json указана версия, отличная от 1, поэтому объявление проигнорировано',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json не удалось разобрать (ошибка JSON), поэтому объявление проигнорировано',\n 'canvas.skills.configIssueEmpty': 'В skill.config.json нет пригодных полей или шагов, поэтому он проигнорирован',\n}\n","/**\n * Locale dictionaries and the pure interpolator.\n *\n * This module is the React-free half of the plugin's copy layer: the host half\n * imports the very same dictionaries to render server-produced text (skill\n * catalog labels, error messages, produced node titles) in the language the\n * user picked in the DSH interface. The browser half layers the live locale\n * binding (and its change subscription) on top in `helpers.ts`.\n *\n * Both halves must agree on key names, so the tables live here — not in a file\n * that also holds React state, which the two bundles would instantiate twice.\n */\n\nimport { en, ru, zh, type ImageGenKey } from './client/locales.ts'\n\n/** Languages with a shipped dictionary. */\nexport type ImageGenLanguage = 'zh' | 'en' | 'ru'\n\n/** Template values accepted by the interpolator. */\nexport type TranslateValues = Record<string, string | number>\n\n/** Every shipped dictionary, keyed by language. */\nexport const IMAGEGEN_DICTIONARIES: Record<ImageGenLanguage, Record<string, string>> = { zh, en, ru }\n\n/** Normalize any DSH locale id onto a shipped dictionary (unknown → English). */\nexport function imageGenLanguageOf(value: unknown): ImageGenLanguage {\n const head = typeof value === 'string' ? value.trim().toLowerCase().split(/[-_]/)[0] ?? '' : ''\n return head === 'zh' || head === 'ru' ? head : 'en'\n}\n\n/**\n * Translate one key with optional `{name}` template params, falling back to\n * Chinese (the key source) and finally to the key itself.\n * @param key - dictionary key.\n * @param values - optional `{name}` substitutions.\n * @param language - shipped language to resolve against.\n * @returns the rendered copy.\n */\nexport function interpolate(key: string, values: TranslateValues | undefined, language: ImageGenLanguage): string {\n const text = IMAGEGEN_DICTIONARIES[language][key] ?? zh[key as ImageGenKey] ?? key\n if (values === undefined) return text\n let rendered: string = text\n for (const [name, value] of Object.entries(values)) {\n rendered = rendered.replaceAll(`{${name}}`, String(value))\n }\n return rendered\n}\n","/**\n * Shared panel helpers: the active-dictionary pick bound to the dsh-imagegen\n * interpolator, the plugin locale that follows the DSH interface language\n * (bridged in client/index.ts from ctx.locale — the plugin ships zh / en / ru\n * and registers itself as a DSH language pack for Русский), plus a small\n * error-message extractor. All copy stays in the locale dictionaries.\n *\n * The dictionaries themselves live in ../locale-tables.ts so the host half can\n * render the same copy without duplicating the tables.\n */\n\nimport { imageGenLanguageOf, interpolate, type ImageGenLanguage, type TranslateValues } from '../locale-tables.ts'\nimport type { ImageGenKey } from './locales.ts'\n\nexport type { ImageGenLanguage, TranslateValues }\n\n/** The active DSH locale mapped onto our dictionary (module-level, one value per app). */\nlet activeLocale: ImageGenLanguage = 'zh'\n\n/** Bumped on every locale change; useSyncExternalStore version. */\nlet languageVersion = 0\n\nconst languageListeners = new Set<() => void>()\n\n/**\n * Adopt the DSH interface language. Unknown ids (future language packs)\n * resolve to English — the same per-key fallback convention the host locale\n * chain uses.\n */\nexport function applyHostLocale(id: unknown): void {\n const next = imageGenLanguageOf(id)\n if (next === activeLocale) return\n activeLocale = next\n languageVersion += 1\n for (const listener of [...languageListeners]) listener()\n}\n\n/** Monotonic version of the active locale (external-store snapshot). */\nexport function getImageGenLanguageVersion(): number {\n return languageVersion\n}\n\n/** Observe locale changes; returns the unsubscriber. */\nexport function subscribeImageGenLanguage(listener: () => void): () => void {\n languageListeners.add(listener)\n return () => { languageListeners.delete(listener) }\n}\n\n/** The active shipped language (also used to translate host-sent copy). */\nexport function activeImageGenLanguage(): ImageGenLanguage {\n return activeLocale\n}\n\n/** Translate a key with optional {name} template params (current language). */\nexport function tt(key: ImageGenKey, values?: TranslateValues): string {\n return interpolate(key, values, activeLocale)\n}\n\n/** Human-readable error text from an unknown thrown value. */\nexport function errorMessage(error: unknown): string {\n if (error instanceof Error) return error.message\n return String(error)\n}\n","/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Self-contained slice of the same\n * pattern the dsh-web-ui family cards use (this package must not depend on a\n * sibling UI package).\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope, SettingsScopeSnapshot } from './settings-scope.ts'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n /**\n * True for secret fields (role('secret') in the namespace schema): the\n * redacted wire view never returns the stored value, so the form treats an\n * empty draft as \"no change\" and judges writes by the namespace's secrets\n * sidecar instead of the user layer.\n */\n secret?: boolean\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /** Whether the namespace is actually served (the bridge answered). */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** A newline/comma-separated model list, persisted as a normalized string array. */\nexport function stringListField(field: string): FieldSpec {\n return {\n field,\n format: value => Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string').join('\\n') : '',\n parse: (text) => {\n const values = [...new Set(text.split(/[\\n,]/).map(item => item.trim()).filter(Boolean))]\n return values.length === 0 ? { kind: 'clear' } : { kind: 'set', value: values }\n },\n }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n if (text === 'true') return { kind: 'set', value: true }\n if (text === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/**\n * A secret field (role('secret') in the namespace schema). The stored value is\n * never rendered or returned by the redacted wire view, so:\n * - an empty draft means \"no change\" (typing nothing must never clear an\n * invisible stored key); the dedicated clear action stages an explicit clear;\n * - a write's outcome is judged by the namespace's secrets sidecar through\n * the {@link CardForm} `secretSettled` hook, never by the user layer.\n */\nexport function secretField(field: string): FieldSpec {\n return {\n field,\n secret: true,\n format: () => '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return undefined\n return { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings scope and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n /**\n * @param scope - the bound settings scope for this card's namespace.\n * @param specs - the fields this card edits.\n * @param options.secretSettled - for secret fields, whether the namespace\n * currently holds a stored secret (the redacted view never round-trips the\n * value, so a write's outcome is read from the secrets sidecar instead).\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n private readonly options: { secretSettled?: (field: string) => boolean } = {},\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n scope.subscribe(() => { this.publish() })\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n // A secret field's empty draft is \"no change\" (see secretField), never\n // an invalid state — the stored value is invisible, so the user cannot\n // be expected to type over it.\n invalid: write === undefined && !(spec.secret === true && staged.text.trim() === ''),\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n */\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field)\n if (staged.clear) {\n const present = spec.secret === true\n ? (this.options.secretSettled?.(field) ?? false)\n : this.stored(field)\n if (present) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n const spec = this.specOf(field)\n if (spec.secret === true) return !(this.options.secretSettled?.(field) ?? false)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n const spec = this.specOf(field)\n if (spec.secret === true) return this.options.secretSettled?.(field) ?? true\n return this.userLayer()?.[field] === value\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n","/**\n * Staged form model for the channel list of the settings card. Mirrors the\n * CardForm staging pattern (dirty → one save) but for a structured value, so\n * the card can edit N channels, per-channel keys, and the default-channel\n * flag, then persist everything in one revision-fenced mutate call.\n *\n * Storage rules (dictated by dsh-settings semantics):\n * - the whole `channels` array is written wholesale via `path: ['channels']`;\n * - every channel's API key lives at `channelSecrets.<channelId>` (a secret\n * dict), written per-key so untouched keys are never clobbered by a save\n * the reader could not see (keys are redacted out of the wire view);\n * - path ops never navigate *inside* the channels array.\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { ChannelConfig, ChannelProtocolPreference, ModelMapping, SubscriptionProvider } from '../protocol.ts'\nimport type { ImageGenScope, SettingsOp } from './settings-scope.ts'\n\n/** One channel as the editor stages it (secrets never travel here). */\nexport interface ChannelDraft {\n id: string\n preset: string\n name: string\n apiUrl: string\n apiUrlFull: boolean\n protocol?: ChannelProtocolPreference\n auth?: 'api-key' | 'subscription'\n subscription?: SubscriptionProvider\n models: ModelMapping[]\n}\n\n/** One staged key edit for a channel. */\nexport type KeyEdit = { kind: 'set'; value: string } | { kind: 'clear' }\n\n/** The state the card renders. */\nexport interface ChannelsFormState {\n /** Staged channel list (the scope value when nothing is staged). */\n channels: ChannelDraft[]\n /** Which channels currently hold a stored secret (staged edits included). */\n keySet: Record<string, boolean>\n /** The effective default channel id. */\n defaultChannelId: string\n /** The effective default image-model alias (first model when unset). */\n defaultModel: string\n /** Whether a save would write anything. */\n dirty: boolean\n /** Whether the document accepts writes. */\n writable: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save failed (cleared by the next edit or save). */\n failed: boolean\n}\n\n/** The actions the card's slot entry injects. */\nexport interface ChannelsFormActions {\n /** Replace the whole channel list (add/edit/remove go through here). */\n setChannels: (channels: ChannelDraft[]) => void\n /** Stage a key for one channel ('' clears; undefined = no staged change). */\n setChannelKey: (id: string, value: string | undefined) => void\n /** Stage the default-channel flag. */\n setDefaultChannel: (id: string) => void\n /** Stage the default image-model alias. */\n setDefaultModel: (model: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n commit: () => Promise<void>\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** Deep equality over JSON-compatible data (the change predicate). */\nfunction deepEqualJson(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false\n return a.every((entry, index) => deepEqualJson(entry, b[index]))\n }\n const left = a as Record<string, unknown>\n const right = b as Record<string, unknown>\n const keys = Object.keys(left)\n if (keys.length !== Object.keys(right).length) return false\n return keys.every(key => key in right && deepEqualJson(left[key], right[key]))\n}\n\n/** Trim and normalize a draft channel (models never carry empty aliases). */\nfunction stripChannel(channel: ChannelDraft): ChannelDraft {\n const models = channel.models\n .map(model => ({ alias: model.alias.trim(), id: model.id.trim() === '' ? model.alias.trim() : model.id.trim() }))\n .filter(model => model.alias !== '')\n return {\n id: channel.id,\n preset: channel.preset,\n name: channel.name.trim(),\n apiUrl: channel.apiUrl.trim(),\n apiUrlFull: channel.apiUrlFull === true,\n ...channel.protocol === undefined ? {} : { protocol: channel.protocol },\n ...channel.auth === undefined ? {} : { auth: channel.auth },\n ...channel.subscription === undefined ? {} : { subscription: channel.subscription },\n models: [...new Map(models.map(model => [model.alias, model])).values()],\n }\n}\n\nexport class ChannelsForm {\n private stagedChannels: ChannelDraft[] | null = null\n private readonly stagedKeys = new Map<string, KeyEdit>()\n private stagedDefault: string | null = null\n private stagedDefaultModel: string | null = null\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n constructor(private readonly scope: ImageGenScope) {\n scope.subscribe(() => { this.publish() })\n scope.subscribeSecretSets(() => { this.publish() })\n }\n\n /** Publish a projection of this form, rebuilt on every scope or draft change. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Subscribe to staged and persisted channel changes. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /** The staged channel list, or the scope value when nothing is staged. */\n private channelsValue(): ChannelDraft[] {\n const view = this.scope.getSnapshot().value as { channels?: ChannelConfig[] } | undefined\n return this.stagedChannels ?? (Array.isArray(view?.channels) ? view.channels.map(toDraft) : [])\n }\n\n /** Whether a channel currently holds a stored or staged secret. */\n private keyHeld(id: string): boolean {\n const edit = this.stagedKeys.get(id)\n if (edit !== undefined) return edit.kind === 'set' && edit.value !== ''\n return this.scope.getSecretSetSnapshot(`channelSecrets.${id}`)\n }\n\n private defaultValue(): string {\n if (this.stagedDefault !== null) return this.stagedDefault\n const view = this.scope.getSnapshot().value as { defaultChannelId?: string } | undefined\n const channels = this.channelsValue()\n if (view?.defaultChannelId !== undefined && channels.some(channel => channel.id === view.defaultChannelId)) return view.defaultChannelId\n return channels[0]?.id ?? ''\n }\n\n private modelAliases(): string[] {\n return [...new Set(this.channelsValue().flatMap(channel => channel.models.map(model => model.alias)).filter(alias => alias !== ''))]\n }\n\n private defaultModelValue(): string {\n if (this.stagedDefaultModel !== null) return this.stagedDefaultModel\n const models = this.modelAliases()\n const view = this.scope.getSnapshot().value as { defaultModel?: string } | undefined\n if (typeof view?.defaultModel === 'string' && models.includes(view.defaultModel)) return view.defaultModel\n return models[0] ?? ''\n }\n\n private dirtyValue(): boolean {\n const channels = this.channelsValue()\n const stagedChanged = this.stagedChannels !== null && !deepEqualJson(this.stagedChannels, scopeChannelsOf(this.scope))\n const scopeView = this.scope.getSnapshot().value as { defaultChannelId?: string; defaultModel?: string } | undefined\n const scopeDefault = scopeView?.defaultChannelId ?? channels[0]?.id ?? ''\n const defaultChanged = this.stagedDefault !== null && this.stagedDefault !== scopeDefault\n const models = this.modelAliases()\n const storedModel = typeof scopeView?.defaultModel === 'string' && models.includes(scopeView.defaultModel)\n ? scopeView.defaultModel\n : models[0] ?? ''\n const modelChanged = this.stagedDefaultModel !== null && this.stagedDefaultModel !== storedModel\n return stagedChanged || defaultChanged || modelChanged || this.stagedKeys.size > 0\n }\n\n /** The card-facing snapshot. */\n snapshot(): ChannelsFormState {\n const channels = this.channelsValue()\n const keySet: Record<string, boolean> = {}\n for (const channel of channels) keySet[channel.id] = this.keyHeld(channel.id)\n return {\n channels,\n keySet,\n defaultChannelId: this.defaultValue(),\n defaultModel: this.defaultModelValue(),\n dirty: this.dirtyValue(),\n writable: this.scope.getSnapshot().writable !== false,\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): ChannelsFormActions {\n return {\n setChannels: (channels) => { this.stageChannels(channels) },\n setChannelKey: (id, value) => { this.stageKey(id, value) },\n setDefaultChannel: (id) => { this.stagedDefault = id; this.failed = false; this.publish() },\n setDefaultModel: (model) => { this.stagedDefaultModel = model; this.failed = false; this.publish() },\n commit: () => this.commit(),\n discard: () => {\n if (this.stagedChannels === null && this.stagedKeys.size === 0 && this.stagedDefault === null && this.stagedDefaultModel === null && !this.failed) return\n this.stagedChannels = null\n this.stagedKeys.clear()\n this.stagedDefault = null\n this.stagedDefaultModel = null\n this.failed = false\n this.publish()\n },\n }\n }\n\n // ------------------------------------------------------------------ staging\n\n private stageChannels(channels: ChannelDraft[]): void {\n const cleaned = channels.map(stripChannel)\n this.stagedChannels = cleaned\n this.failed = false\n this.publish()\n }\n\n private stageKey(id: string, value: string | undefined): void {\n if (value === undefined || value.trim() === '') {\n if (this.keyHeld(id)) this.stagedKeys.set(id, { kind: 'clear' })\n // An empty key for a key-less channel stages nothing.\n } else {\n this.stagedKeys.set(id, { kind: 'set', value: value.trim() })\n }\n this.failed = false\n this.publish()\n }\n\n // ----------------------------------------------------------------- save\n\n /** Build the single batch of path ops a save performs. */\n private planOps(): SettingsOp[] {\n const ops: SettingsOp[] = []\n if (this.stagedChannels !== null) {\n ops.push({ op: 'set', path: ['channels'], value: this.stagedChannels })\n // Once channels exist, the legacy flat fields are obsolete (idempotent).\n ops.push({ op: 'unset', path: ['apiUrl'] })\n ops.push({ op: 'unset', path: ['apiKey'] })\n ops.push({ op: 'unset', path: ['imageModels'] })\n }\n for (const [id, edit] of this.stagedKeys) {\n if (edit.kind === 'set') ops.push({ op: 'set', path: ['channelSecrets', id], value: edit.value })\n else ops.push({ op: 'unset', path: ['channelSecrets', id] })\n }\n if (this.stagedDefault !== null) {\n ops.push({ op: 'set', path: ['defaultChannelId'], value: this.stagedDefault })\n }\n if (this.stagedDefaultModel !== null) {\n ops.push(this.stagedDefaultModel === ''\n ? { op: 'unset', path: ['defaultModel'] }\n : { op: 'set', path: ['defaultModel'], value: this.stagedDefaultModel })\n }\n return ops\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after the write settles.\n */\n async commit(): Promise<void> {\n if (this.saving) return\n const ops = this.planOps()\n if (ops.length === 0) return\n this.saving = true\n this.failed = false\n this.publish()\n try {\n await this.scope.mutateOps(ops)\n this.stagedChannels = null\n this.stagedKeys.clear()\n this.stagedDefault = null\n this.stagedDefaultModel = null\n this.failed = false\n } catch {\n this.failed = true\n } finally {\n this.saving = false\n this.publish()\n }\n }\n\n private publish(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n\n/** Project a stored channel into a draft (secrets never travel in channels). */\nfunction toDraft(channel: ChannelConfig): ChannelDraft {\n return { id: channel.id, preset: channel.preset, name: channel.name, apiUrl: channel.apiUrl, apiUrlFull: channel.apiUrlFull === true, ...channel.protocol === undefined ? {} : { protocol: channel.protocol }, ...channel.auth === undefined ? {} : { auth: channel.auth }, ...channel.subscription === undefined ? {} : { subscription: channel.subscription }, models: channel.models.map(model => ({ ...model })) }\n}\n\n/** The scope's current channels value (a plain array), for change detection. */\nfunction scopeChannelsOf(scope: ImageGenScope): unknown {\n const view = scope.getSnapshot().value as { channels?: unknown } | undefined\n return Array.isArray(view?.channels) ? view.channels : []\n}\n","/**\n * Model catalog: protocol-family detection and capability annotation shared by\n * the engine, the settings card, and the panel. Known id patterns map onto the\n * OpenAI-compatible image protocol families this plugin shapes requests for;\n * anything unrecognized falls back to the generic OpenAI protocol (best\n * effort) and is flagged as unknown so the UI can warn without blocking.\n *\n * Framework-free (pure data + regex), safe for the client bundle to inline.\n */\n\nexport type ModelFamily = 'gpt-image' | 'dall-e' | 'grok' | 'nanobanana' | 'seedream' | 'zhipu' | 'qwen' | 'minimax' | 'unknown'\n\n/** Capability/identity annotation for one model id. */\nexport interface ModelCatalogEntry {\n /** Family the request is shaped for. */\n family: ModelFamily\n /** Short badge label (en). */\n label: string\n /** Short badge label (zh). */\n labelZh: string\n /** Whether the family is known (false = best-effort OpenAI protocol). */\n known: boolean\n /** Whether the family supports image-to-image edits. */\n supportsEdit: boolean\n /** Whether the family natively takes aspect-ratio dials. */\n supportsAspectRatio: boolean\n /** Quality tiers the family interprets natively. */\n qualityTiers: string[]\n}\n\nconst ENTRIES: Record<Exclude<ModelFamily, 'unknown'>, Omit<ModelCatalogEntry, 'family'>> = {\n 'gpt-image': {\n label: 'gpt-image',\n labelZh: 'GPT 图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: false,\n qualityTiers: ['1K', '2K', '4K'],\n },\n 'dall-e': {\n label: 'DALL·E',\n labelZh: 'DALL·E',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: false,\n qualityTiers: ['auto'],\n },\n grok: {\n label: 'grok',\n labelZh: 'Grok',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K'],\n },\n nanobanana: {\n label: 'nanobanana',\n labelZh: 'Nano Banana',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K', '4K'],\n },\n seedream: {\n label: 'seedream',\n labelZh: 'Seedream',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K'],\n },\n zhipu: {\n label: 'GLM-Image',\n labelZh: '智谱图像',\n known: true,\n supportsEdit: false,\n supportsAspectRatio: false,\n qualityTiers: ['HD'],\n },\n qwen: {\n label: 'qwen-image',\n labelZh: '千问图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['auto'],\n },\n minimax: {\n label: 'MiniMax',\n labelZh: 'MiniMax 图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['auto'],\n },\n}\n\n/** Documented upstream prompt hard limits (UTF-16 code units). The engine\n * fast-fails on these before any network call and the panel counter shows\n * them live; families absent here have no documented limit. */\nconst PROMPT_CHAR_LIMITS: Partial<Record<Exclude<ModelFamily, 'unknown'>, number>> = {\n // MiniMax image-01 rejects prompts >= 1500 chars upstream (base_resp 2013).\n minimax: 1500,\n}\n\n/** The documented prompt character limit for a model id, or null when the\n * family has no known limit. Single source of truth for engine enforcement\n * and the panel's live counter. */\nexport function promptCharLimit(model: string): number | null {\n const { family } = describeModel(model)\n return (family !== 'unknown' ? PROMPT_CHAR_LIMITS[family] : undefined) ?? null\n}\n\n/** Official Gemini image ids served by Nano Banana gateways. */\nconst NANOBANANA_GEMINI_IDS = new Set([\n 'gemini-3-pro-image',\n 'gemini-3-pro-image-preview',\n 'gemini-3.1-flash-image',\n 'gemini-3.1-flash-image-preview',\n 'gemini-3.1-flash-lite-image',\n 'gemini-2.5-flash-image',\n])\n\n/** Classify one upstream model id into its request-shaping family. */\nexport function describeModel(model: string): ModelCatalogEntry {\n const id = model.trim()\n if (/^gpt-image/i.test(id)) return { family: 'gpt-image', ...ENTRIES['gpt-image'] }\n if (/^dall-e/i.test(id)) return { family: 'dall-e', ...ENTRIES['dall-e'] }\n if (/^grok-imagine(?:-|$)/.test(id)) return { family: 'grok', ...ENTRIES.grok }\n if (/^nanobanana/i.test(id) || NANOBANANA_GEMINI_IDS.has(id)) return { family: 'nanobanana', ...ENTRIES.nanobanana }\n if (/^(?:doubao-)?seedream/i.test(id)) return { family: 'seedream', ...ENTRIES.seedream }\n if (/^(?:glm-image|cogview(?:-|$))/i.test(id)) return { family: 'zhipu', ...ENTRIES.zhipu }\n if (/^qwen-image(?:[-_.]|$)/i.test(id)) return { family: 'qwen', ...ENTRIES.qwen }\n if (/^(?:minimax[-_/])?image-\\d+/i.test(id)) return { family: 'minimax', ...ENTRIES.minimax }\n return { family: 'unknown', label: 'unknown', labelZh: '未知协议', known: false, supportsEdit: true, supportsAspectRatio: false, qualityTiers: [] }\n}\n\n/** Conservative fallback for providers whose /models response only has ids.\n * Metadata-aware filtering lives in prompt-enhancer.ts; this catches common\n * image model naming conventions without treating every unknown model as an\n * image model. */\nexport function isLikelyImageModelId(model: string): boolean {\n return /(?:^|[-_.])(?:image|img|diffusion|flux|cogview|imagen|seedream|nanobanana|grok-imagine|dall-e|stable-diffusion|sdxl|pixart|kolors|ideogram|midjourney|recraft|hunyuan|jimeng|wanx|hidream|playground)(?:$|[-_.])/i.test(model.trim())\n}\n\n/** The family a model id routes its request through. */\nexport function modelFamily(model: string): ModelFamily {\n return describeModel(model).family\n}\n","/**\n * Wire contract shared by the host and client halves of dsh-imagegen: the\n * settings namespace, the route paths, and the generate payload/result shapes.\n * Pure types + constants 鈥?safe for the client bundle to inline.\n */\n\n/** Settings namespace this plugin owns (host settings seam + bridge). */\nexport const IMAGEGEN_SETTINGS_NAMESPACE = 'imagegen'\n\n/** Published package version shared by the host updater and the client UI. */\nexport const PLUGIN_VERSION = '1.5.13'\n\n/** Same-origin route family (loopback-only, mirroring the dsh-ssh fence). */\nexport const SETTINGS_API = {\n describe: '/api/dsh-imagegen/settings/describe',\n mutate: '/api/dsh-imagegen/settings/mutate',\n} as const\n\n/** Subscription account login and status routes. */\nexport const SUBSCRIPTION_API = {\n status: '/api/dsh-imagegen/subscription/status',\n login: '/api/dsh-imagegen/subscription/login',\n} as const\n\n/** Official/subscription-backed image providers. */\nexport const SUBSCRIPTION_PROVIDERS = ['chatgpt-sub', 'grok-sub', 'google-sub', 'openrouter-sub'] as const\nexport type SubscriptionProvider = typeof SUBSCRIPTION_PROVIDERS[number]\n\n/** Transport protocol a channel uses to reach its gateway. */\nexport type ChannelProtocol = 'images' | 'chat-completions'\n\n/** Stored preference: auto keeps old channels on images unless the URL is explicit. */\nexport type ChannelProtocolPreference = 'auto' | ChannelProtocol\n\n/** Whether an endpoint URL explicitly names an OpenAI-compatible chat route. */\nexport function isChatCompletionsUrl(value: string): boolean {\n const trimmed = value.trim()\n if (trimmed === '') return false\n try {\n return /\\/chat\\/completions\\/?$/.test(new URL(trimmed).pathname)\n } catch {\n return /\\/chat\\/completions\\/?$/.test(trimmed.split(/[?#]/, 1)[0] ?? '')\n }\n}\n\n/** Whether a stored value is one of the accepted protocol preferences. */\nexport function isChannelProtocolPreference(value: unknown): value is ChannelProtocolPreference {\n return value === 'auto' || value === 'images' || value === 'chat-completions'\n}\n\n/** Resolve a channel preference without changing legacy behavior by default. */\nexport function resolveChannelProtocol(apiUrl: string, preference: ChannelProtocolPreference | undefined = 'auto'): ChannelProtocol {\n if (preference === 'images' || preference === 'chat-completions') return preference\n return isChatCompletionsUrl(apiUrl) ? 'chat-completions' : 'images'\n}\n\n/** Subscription channels with a fixed image model. */\nexport const DEFAULT_SUBSCRIPTION_MODELS: Record<SubscriptionProvider, string> = {\n 'chatgpt-sub': 'gpt-image-2.5-flare',\n 'grok-sub': 'grok-imagine-image-2.0',\n 'google-sub': 'gemini-3-pro-image',\n 'openrouter-sub': 'google/gemini-3-pro-image',\n}\n\n/** Human-facing provider names used in status and errors. */\nexport const SUBSCRIPTION_PROVIDER_DISPLAY_NAMES: Record<SubscriptionProvider, string> = {\n 'chatgpt-sub': 'ChatGPT 订阅',\n 'grok-sub': 'Grok 订阅',\n 'google-sub': 'Google 订阅',\n 'openrouter-sub': 'OpenRouter 账号',\n}\n\n/** Interfaces that are supported experimentally and should be labelled in the UI. */\nexport const EXPERIMENTAL_SUBSCRIPTION_PROVIDERS: ReadonlySet<SubscriptionProvider> = new Set(['chatgpt-sub', 'google-sub'])\n\n/** Whether a raw string names one of the subscription providers. */\nexport function isSubscriptionProvider(value: unknown): value is SubscriptionProvider {\n return typeof value === 'string' && (SUBSCRIPTION_PROVIDERS as readonly string[]).includes(value)\n}\n\n/** The image-generation proxy route. */\nexport const GENERATE_API = '/api/dsh-imagegen/generate'\n\n/** Host-mediated OpenAI-compatible prompt enhancement endpoints. */\nexport const PROMPT_ENHANCE_API = {\n models: '/api/dsh-imagegen/prompt-enhance/models',\n enhance: '/api/dsh-imagegen/prompt-enhance',\n} as const\n\n/** Host-mediated candidate discovery for the configured image API. */\nexport const IMAGE_MODEL_API = {\n models: '/api/dsh-imagegen/image-models',\n} as const\n\n/** Host-served built-in provider catalog (channels the user can instantiate). */\nexport const PRESETS_API = '/api/dsh-imagegen/presets' as const\n\n/** Loopback-only image reader for Agent tool-result previews. */\nexport const AGENT_IMAGE_API = '/api/dsh-imagegen/agent-image' as const\n\n/** Store the current composer image for the direct edit_image command. */\nexport const CONVERSATION_IMAGE_API = '/api/dsh-imagegen/conversation-image' as const\n\n/** Host-resident generation queue endpoints. */\nexport const TASK_API = {\n submit: '/api/dsh-imagegen/tasks/submit',\n list: '/api/dsh-imagegen/tasks/list',\n get: '/api/dsh-imagegen/tasks/get',\n cancel: '/api/dsh-imagegen/tasks/cancel',\n retry: '/api/dsh-imagegen/tasks/retry',\n} as const\n\n/** Generation modes. */\nexport type GenerateMode = 'text' | 'edit'\n\n/** A client → host generate request (what the panel collects). */\nexport interface GenerateRequest {\n /** text-to-image (images/generations) or image-to-image (images/edits). */\n mode: GenerateMode\n /**\n * User-facing model name (an alias from the channel's model catalog). The\n * host maps it onto the configured channel and fills `upstream` with the\n * real id before the engine sees it.\n */\n model: string\n /** The prompt. Upstream providers may impose their own length limits. */\n prompt: string\n /** Canvas size as an aspect ratio: 'auto' or e.g. '1:1' / '16:9' / '21:9'.\n * The host maps it onto each model's own vocabulary (aspect_ratio for Grok\n * and Nano Banana, resolution-tier size for Seedream, the closest pixel size for\n * OpenAI-compatible endpoints). */\n size: string\n /** Clarity tier: 'auto' | '1k' | '2k' | '4k'. The host maps it onto the\n * model's own vocabulary (resolution for Grok, image_size for Nano Banana,\n * and size for Seedream,\n * Nano Banana, quality for OpenAI). */\n quality: string\n /** Number of images, 1-4. */\n n: number\n /**\n * Passthrough detail parameter: '' (omit), 'standard', or 'high'. Some\n * gpt-image-2 gateways expose it; official OpenAI endpoints reject unknown\n * parameters, so the UI defaults to '' (omit).\n */\n detail: string\n /** Reference image as a data URL (edit mode only). */\n image?: string\n /** Additional reference images as data URLs (edit mode only). The first\n * image stays in `image`; providers that accept several references get them\n * all, single-reference providers see `image` alone. */\n images?: string[]\n /** Original reference-image name, retained in the history entry. */\n refName?: string\n /** Channel this request targets (the host falls back to the default when\n * absent, and re-routes by model alias when the alias lives elsewhere). */\n channelId?: string\n /** Channel display name snapshot, kept on the history entry (host-filled). */\n channel?: string\n /** Upstream model id actually sent to the gateway (host-filled from the\n * alias mapping; defaults to `model` when absent). */\n upstream?: string\n}\n\n/** One generated image, normalized host-side to base64 so the browser never\n * has to fetch the upstream (no CORS, no key exposure). */\nexport interface GeneratedImage {\n /** Raw base64 payload (no data: prefix). */\n b64: string\n /** MIME type of the payload, e.g. image/png. */\n mime: string\n /** Upstream revised prompt, when provided. */\n revisedPrompt?: string\n}\n\n/** Successful generate outcome. */\nexport interface GenerateResult {\n images: GeneratedImage[]\n}\n\n/**\n * One model mapping in a channel's catalog: the display alias the user, the\n * panel, and the Agent see, and the upstream model id actually sent to the\n * gateway. The alias defaults to the upstream id but can be renamed freely.\n */\nexport interface ModelMapping {\n /** User-facing model name (defaults to the upstream id). */\n alias: string\n /** Upstream model id sent to the gateway. */\n id: string\n}\n\n/**\n * One configured image channel (provider). Secrets never live here — the API\n * key is stored at `channelSecrets.<channelId>` in the settings document so\n * whole-array writes can never clobber keys the user did not re-enter.\n */\nexport interface ChannelConfig {\n /** Stable channel id (the channelSecrets dict is keyed by it). */\n id: string\n /** Preset provider id this channel was created from ('' = custom). */\n preset: string\n /** Display name shown in the list, the panel, and Agent guidance. */\n name: string\n /** OpenAI-compatible base URL, or the exact generation URL when apiUrlFull is true. */\n apiUrl: string\n /** Use apiUrl verbatim instead of appending /images/generations or /images/edits. */\n apiUrlFull: boolean\n /** Request protocol; 'auto' infers chat-completions from an exact chat URL. */\n protocol?: ChannelProtocolPreference\n /** Authentication source; absent means the legacy API-key path. */\n auth?: 'api-key' | 'subscription'\n /** Subscription provider when auth is subscription. */\n subscription?: SubscriptionProvider\n /** The channel's model catalog (alias → upstream id). */\n models: ModelMapping[]\n}\n\n/** One built-in provider as the settings card consumes it. */\nexport interface PresetProviderView {\n id: string\n name: string\n apiUrl: string\n hint: string\n models: ModelMapping[]\n /** Set for subscription-backed presets. */\n subscription?: SubscriptionProvider\n /** True when the subscription relies on an undocumented interface. */\n experimental?: boolean\n}\n\nexport type GenerationTaskStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'\n\nexport interface GenerationTask {\n id: string\n request: GenerateRequest\n status: GenerationTaskStatus\n createdAt: number\n startedAt?: number\n finishedAt?: number\n result?: GenerateResult\n error?: string\n}\n\n/**\n * Wire shape for the queue poll. Reference-image and result payloads stay\n * host-side; the client hydrates a completed task once through TASK_API.get.\n */\nexport interface GenerationTaskSummary extends Omit<GenerationTask, 'result'> {\n resultAvailable: boolean\n}\n","/**\n * React binding for the plugin locale, which follows the DSH interface\n * language (bridged from ctx.locale in client/index.ts). Surfaces that render\n * tt() output subscribe to this tick so a DSH language switch re-renders them\n * immediately.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport { getImageGenLanguageVersion, subscribeImageGenLanguage } from './helpers.ts'\n\n/** Re-render the calling component whenever the DSH language changes. */\nexport function useImageGenLanguageTick(): number {\n return useSyncExternalStore(subscribeImageGenLanguage, getImageGenLanguageVersion)\n}\n","/**\n * The dsh-imagegen settings card: channel management (list rows with status\n * dots, an editor dialog with the model-catalog alias → upstream mapping, and\n * built-in provider presets), plus the prompt-enhancement model and the plugin\n * switches. Registers as the row's configuration entry on the Plugins page\n * (`plugins.row.config`), independent of the dsh-web-ui family group, bound to\n * the plugin's own bridge settings scope.\n *\n * The interaction mirrors the host's model-provider page: one row per channel\n * (status dot + edit/delete), two add buttons (built-in provider / custom),\n * and an editor holding API key, display name, API URL, and the model catalog\n * with detection.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { CardForm, booleanField, secretField, textField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport { ChannelsForm, type ChannelDraft, type ChannelsFormActions, type ChannelsFormState } from './channels-form.ts'\nimport type { ImageGenScope } from './settings-scope.ts'\nimport { describeModel } from '../model-catalog.ts'\nimport { IMAGE_MODEL_API, PRESETS_API, PROMPT_ENHANCE_API, type ModelMapping, type PresetProviderView } from '../protocol.ts'\nimport type { ImageGenKey } from './locales.ts'\nimport { tt, type TranslateValues } from './helpers.ts'\nimport { useImageGenLanguageTick } from './use-language.ts'\nimport css from './settings-card.module.css'\n\n/** The global (non-channel) fields this card's staged form edits. */\nexport interface ImageGenSettings {\n enabled?: boolean\n announceToAgent?: boolean\n allowAgentImageGeneration?: boolean\n promptApiUrl?: string\n promptApiKey?: string\n promptModel?: string\n}\n\n/** What the card renders. */\nexport interface ImageGenSettingsCardState extends CardShell {\n /** Channel list staging (channels + per-channel key edits + default). */\n channels: ChannelsFormState\n /** Master switch. */\n enabled: CardFieldState\n /** System-prompt announcement flag. */\n announceToAgent: CardFieldState\n allowAgentImageGeneration: CardFieldState\n promptApiUrl: CardFieldState\n promptApiKey: CardFieldState\n promptModel: CardFieldState\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface ImageGenSettingsCardFace extends CardActions {\n /** Channel staging actions (committed together with the card's save). */\n channels: ChannelsFormActions\n hooks: {\n /** Card snapshot bound by the renderer as useImageGenSettingsCard. */\n imageGenSettingsCard: SnapshotStore<ImageGenSettingsCardState>\n }\n}\n\n/** Bridges the imagegen scope onto the card's staged forms. */\nexport class ImageGenSettingsCardController {\n private readonly form: CardForm<ImageGenSettings>\n private readonly channelsForm: ChannelsForm\n\n /** @param scope - the bound bridge scope for the dsh-imagegen namespace. */\n constructor(private readonly scope: ImageGenScope) {\n this.form = new CardForm(scope, [\n booleanField('enabled'),\n booleanField('announceToAgent'),\n booleanField('allowAgentImageGeneration'),\n textField('promptApiUrl'),\n secretField('promptApiKey'),\n textField('promptModel'),\n ], {\n secretSettled: (field) => this.scope.getSecretSetSnapshot(field),\n })\n this.channelsForm = new ChannelsForm(scope)\n }\n\n private projection(): ImageGenSettingsCardState {\n const shell = this.form.shell()\n return {\n ...shell,\n dirty: shell.dirty || this.channelsForm.snapshot().dirty,\n channels: this.channelsForm.snapshot(),\n enabled: this.form.field('enabled'),\n announceToAgent: this.form.field('announceToAgent'),\n allowAgentImageGeneration: this.form.field('allowAgentImageGeneration'),\n promptApiUrl: this.form.field('promptApiUrl'),\n promptApiKey: this.form.field('promptApiKey'),\n promptModel: this.form.field('promptModel'),\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and the form/channel actions.\n */\n inject(): ImageGenSettingsCardFace {\n const cardStore = this.form.bind(() => this.projection())\n this.channelsForm.subscribe(() => { cardStore.set(this.projection()) })\n return {\n hooks: {\n imageGenSettingsCard: cardStore,\n },\n channels: this.channelsForm.actions(),\n ...this.form.actions(),\n }\n }\n}\n\n/** Props the renderer binds for this card. */\nexport type ImageGenSettingsCardProps =\n PropsRuntime<'plugins.row.config'>\n & PropsLocale<'dsh-imagegen'>\n & InjectFace<ImageGenSettingsCardFace>\n\n/**\n * Render the card.\n *\n * The Plugins page renders every configuration entry twice: once as the\n * one-liner under the row's title (`view: 'summary'`) and once as the body of\n * the row's own page (`view: 'page'`). The page draws the row id, the module\n * name, and the crumb itself, so this component supplies only the copy and the\n * form. The summary branch returns before the form's hooks and state so the\n * one-liner does no work.\n *\n * @param props - the view asked for, locale copy, the card snapshot, and the form actions.\n * @returns the one-liner, the card, or nothing while the namespace is loading.\n */\nexport function ImageGenSettingsCard(props: ImageGenSettingsCardProps) {\n // The summary view is the one-liner the page places under the row's title.\n // It returns before any hook or state so it does no work: the page re-renders\n // the entry when the active locale moves, so the copy still follows a\n // language switch without this component subscribing to it.\n if (props.view === 'summary') return tt('settings.summary')\n // The card renders through the plugin's own dictionary so the uiLanguage\n // override applies here too — the host-locale props.t would only follow the\n // DSH interface language.\n const t = tt\n useImageGenLanguageTick()\n const state = props.useImageGenSettingsCard(snapshot => snapshot)\n const [open, setOpen] = useState(false)\n // Global-section local states (prompt enhancement etc.).\n const [promptModels, setPromptModels] = useState<string[]>([])\n const [loadingPromptModels, setLoadingPromptModels] = useState(false)\n const [promptModelsError, setPromptModelsError] = useState<string | null>(null)\n const [manualPromptModelOpen, setManualPromptModelOpen] = useState(false)\n const [manualPromptModel, setManualPromptModel] = useState('')\n const [enhancementOpen, setEnhancementOpen] = useState(false)\n const [promptApiOpen, setPromptApiOpen] = useState(false)\n const [moreOpen, setMoreOpen] = useState(false)\n // Channel list local states.\n const [editingId, setEditingId] = useState<string | null>(null)\n const [presetPickerOpen, setPresetPickerOpen] = useState(false)\n const [presets, setPresets] = useState<PresetProviderView[]>([])\n const [presetError, setPresetError] = useState<string | null>(null)\n const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null)\n\n if (!state.available) return null\n const title = t('settings.title')\n const blocked = !state.dirty || state.invalid || state.saving || state.channels.saving\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n\n if (!state.exposed) {\n return (\n <li className={css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{t('settings.description')}</span>\n </span>\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n\n const channels = state.channels.channels\n const editing = editingId === null ? undefined : channels.find(channel => channel.id === editingId)\n\n return (\n <li className={css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{t('settings.description')}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{t('settings.unsaved')}</span> : null}\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{t('settings.readOnly')}</p> : null}\n\n <section className={css.channelSection} aria-label={t('channels.title')}>\n <div className={css.sectionHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.title')}</h3>\n <p className={css.sectionHint}>{t('channels.hint')}</p>\n </div>\n </div>\n {channels.length === 0\n ? <p className={css.channelEmpty}>{t('channels.empty')}</p>\n : (\n <ul className={css.channelList}>\n {channels.map(channel => {\n const keyHeld = state.channels.keySet[channel.id] === true\n const ready = keyHeld && channel.models.length > 0\n const isDefault = channel.id === state.channels.defaultChannelId\n if (confirmDeleteId === channel.id) {\n return (\n <li key={channel.id} className={css.channelRow} data-action>\n <span className={css.deleteConfirmText}>{t('channels.deleteConfirmTitle', { name: channel.name || t('channels.untitled') })}</span>\n <button type=\"button\" className={css.channelDanger} disabled={disabled} onClick={() => { props.channels.setChannels(channels.filter(candidate => candidate.id !== channel.id)); if (isDefault && channels.length > 1) { const next = channels.find(candidate => candidate.id !== channel.id); if (next !== undefined) props.channels.setDefaultChannel(next.id) } setConfirmDeleteId(null); if (editingId === channel.id) setEditingId(null) }}>{t('channels.confirm')}</button>\n <button type=\"button\" className={css.channelAction} disabled={disabled} onClick={() => { setConfirmDeleteId(null) }}>{t('channels.cancel')}</button>\n </li>\n )\n }\n return (\n <li key={channel.id} className={css.channelRow}>\n <span className={ready ? css.channelDotReady : css.channelDotWarn} aria-hidden=\"true\" title={t(ready ? 'channels.statusReady' : 'channels.statusIncomplete')} />\n <button type=\"button\" className={css.channelMain} disabled={disabled} onClick={() => { setEditingId(channel.id) }}>\n <span className={css.channelName}>{isDefault ? `★ ${channel.name || t('channels.untitled')}` : (channel.name || t('channels.untitled'))}</span>\n <span className={css.channelMeta}>\n <span className={css.channelBadge} data-warn={!keyHeld || channel.models.length === 0 ? '' : undefined}>\n {keyHeld ? t('channels.keySet') : t('channels.keyMissing')}\n {' · '}\n {channel.models.length > 0 ? t('channels.modelCount', { n: channel.models.length }) : t('channels.noModels')}\n </span>\n </span>\n </button>\n <button type=\"button\" className={css.channelAction} onClick={() => { setEditingId(channel.id) }}>{t('channels.edit')}</button>\n <button type=\"button\" className={css.channelAction} data-danger onClick={() => { setConfirmDeleteId(channel.id) }}>{t('channels.delete')}</button>\n </li>\n )\n })}\n </ul>\n )}\n <div className={css.channelControls}>\n {open && presetPickerOpen ? (\n <PresetPicker\n t={t}\n presets={presets}\n error={presetError}\n disabled={state.writable === false}\n onLoad={() => {\n setPresetError(null)\n void fetch(PRESETS_API, { method: 'POST' })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; presets?: PresetProviderView[]; message?: string }\n if (!response.ok || body.ok !== true || body.presets === undefined) throw new Error(body.message ?? `HTTP ${response.status}`)\n setPresets(body.presets)\n })\n .catch(error => { setPresetError(error instanceof Error ? error.message : String(error)) })\n }}\n onPick={(preset) => {\n const draft = newChannelDraft(preset)\n props.channels.setChannels([...channels, draft])\n setPresetPickerOpen(false)\n setEditingId(draft.id)\n }}\n onCustom={() => {\n const draft = newChannelDraft(undefined)\n props.channels.setChannels([...channels, draft])\n setPresetPickerOpen(false)\n setEditingId(draft.id)\n }}\n onClose={() => { setPresetPickerOpen(false) }}\n />\n ) : null}\n\n <div className={css.channelAddRow}>\n <button type=\"button\" className={css.channelAdd} disabled={disabled} onClick={() => { setPresetError(null); setPresetPickerOpen(true) }}>+ {t('channels.addProvider')}</button>\n <button type=\"button\" className={css.channelAdd} disabled={disabled} onClick={() => { addCustomChannel(channels, props.channels, setEditingId) }}>+ {t('channels.addCustom')}</button>\n </div>\n </div>\n </section>\n\n <button\n type=\"button\"\n className={css.disclosure}\n aria-expanded={enhancementOpen}\n onClick={() => { setEnhancementOpen(open => !open) }}\n >\n <span>{t('settings.promptEnhanceTitle')}</span>\n <span>{t('settings.optional')}</span>\n <span aria-hidden=\"true\">{enhancementOpen ? '⌃' : '⌄'}</span>\n </button>\n {enhancementOpen ? <section className={css.optionalContent} aria-label={t('settings.promptEnhanceTitle')}>\n <p className={css.sectionHint}>{t('settings.promptEnhanceHint')}</p>\n <div className={css.sectionHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('settings.promptModel')}</h3>\n <p className={css.sectionHint}>{t('settings.promptModelDetectionHint')}</p>\n </div>\n <button\n type=\"button\"\n className={css.modelFetch}\n disabled={disabled || loadingPromptModels}\n onClick={() => {\n setLoadingPromptModels(true)\n setPromptModelsError(null)\n void fetch(PROMPT_ENHANCE_API.models, { method: 'POST' })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; models?: string[]; message?: string }\n if (!response.ok || body.ok !== true) throw new Error(body.message ?? `HTTP ${response.status}`)\n setPromptModels(body.models ?? [])\n })\n .catch(error => { setPromptModelsError(error instanceof Error ? error.message : String(error)) })\n .finally(() => { setLoadingPromptModels(false) })\n }}\n >\n {loadingPromptModels ? t('settings.promptModelsLoading') : t('settings.promptModelsFetch')}\n </button>\n </div>\n <div className={css.modelSummary}>\n {state.promptModel.text.trim() !== '' ? (\n <span className={css.modelChip}>\n <span>{state.promptModel.text}</span>\n <button type=\"button\" disabled={disabled} aria-label={`${t('settings.removeModel')}: ${state.promptModel.text}`} onClick={() => { props.edit('promptModel', '') }}>×</button>\n </span>\n ) : null}\n <button type=\"button\" className={css.addModel} disabled={disabled} onClick={() => { setManualPromptModelOpen(open => !open); setEnhancementOpen(true) }}>\n {manualPromptModelOpen ? t('settings.cancelAddModel') : t('settings.addModel')}\n </button>\n </div>\n {manualPromptModelOpen ? (\n <div className={css.manualModelRow}>\n <input className={css.input} value={manualPromptModel} placeholder={t('settings.addPromptModelPlaceholder')} disabled={disabled} onChange={event => { setManualPromptModel(event.target.value) }} />\n <button type=\"button\" className={css.addModel} disabled={disabled || manualPromptModel.trim() === ''} onClick={() => { props.edit('promptModel', manualPromptModel); setManualPromptModel('') }}>{t('settings.addModelConfirm')}</button>\n </div>\n ) : null}\n {promptModels.length > 0 ? (\n <div className={css.modelCandidateList} role=\"radiogroup\" aria-label={t('settings.promptModelsCandidates')}>\n <span className={css.modelCandidateLabel}>{t('settings.promptModelsCandidates')}</span>\n {promptModels.map(candidate => (\n <button\n key={candidate}\n type=\"button\"\n role=\"radio\"\n className={css.modelCandidate}\n aria-checked={state.promptModel.text === candidate}\n data-selected={state.promptModel.text === candidate ? '' : undefined}\n disabled={disabled}\n onClick={() => { props.edit('promptModel', candidate) }}\n >\n {candidate}\n </button>\n ))}\n </div>\n ) : null}\n {promptModelsError !== null ? <p className={css.failed} role=\"status\">{promptModelsError}</p> : null}\n <button type=\"button\" className={css.inlineDisclosure} aria-expanded={promptApiOpen} onClick={() => { setPromptApiOpen(open => !open) }}>\n <span>{t('settings.promptApiAdvanced')}</span>\n <span aria-hidden=\"true\">{promptApiOpen ? '⌃' : '⌄'}</span>\n </button>\n {promptApiOpen ? <div className={css.optionalContent}>\n <ValueField\n id=\"dsh-imagegen-settings-prompt-apiurl\"\n label={t('settings.promptApiUrl')}\n hint={t('settings.promptApiUrlHint')}\n placeholder=\"https://api.openai.com/v1\"\n {...fieldProps}\n {...state.promptApiUrl}\n onEdit={(text) => { props.edit('promptApiUrl', text) }}\n onReset={() => { props.resetField('promptApiUrl') }}\n />\n <ValueField\n id=\"dsh-imagegen-settings-prompt-apikey\"\n label={t('settings.promptApiKey')}\n hint={t('settings.promptApiKeyHint')}\n placeholder=\"sk-…\"\n secret\n {...fieldProps}\n {...state.promptApiKey}\n overridden={false}\n onEdit={(text) => { props.edit('promptApiKey', text) }}\n onReset={() => { props.resetField('promptApiKey') }}\n />\n </div> : null}\n </section> : null}\n\n <button type=\"button\" className={css.disclosure} aria-expanded={moreOpen} onClick={() => { setMoreOpen(open => !open) }}>\n <span>{t('settings.moreOptions')}</span>\n <span aria-hidden=\"true\">{moreOpen ? '⌃' : '⌄'}</span>\n </button>\n {moreOpen ? <div className={css.optionalContent}>\n <BooleanField\n id=\"dsh-imagegen-settings-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <BooleanField\n id=\"dsh-imagegen-settings-announce\"\n label={t('settings.announceToAgent')}\n hint={t('settings.announceToAgentHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.announceToAgent}\n onEdit={(text) => { props.edit('announceToAgent', text) }}\n onReset={() => { props.resetField('announceToAgent') }}\n />\n <BooleanField\n id=\"dsh-imagegen-settings-agent-generation\"\n label={t('settings.allowAgentImageGeneration')}\n hint={t('settings.allowAgentImageGenerationHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.allowAgentImageGeneration}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n </div> : null}\n <div className={css.footer}>\n {(state.failed || state.channels.failed) ? <p className={css.failed} role=\"status\">{t('settings.saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving || state.channels.saving}\n onClick={() => { props.discard(); props.channels.discard() }}\n >\n {t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={() => { void props.channels.commit(); void props.save() }}\n >\n {t(!state.saving && !state.channels.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n </div>\n )\n : null}\n\n {open && editing !== undefined ? (\n <ChannelEditor\n key={editing.id}\n t={t}\n channel={editing}\n keyHeld={state.channels.keySet[editing.id] === true}\n otherChannels={channels.filter(channel => channel.id !== editing.id)}\n isDefault={editing.id === state.channels.defaultChannelId}\n writable={state.writable}\n onPatch={(patch) => { replaceChannel(channels, editing.id, patch, props.channels) }}\n onSetModels={(models) => { props.channels.setChannels(channels.map(channel => channel.id === editing.id ? { ...channel, models } : channel)) }}\n onSetKey={(value) => { props.channels.setChannelKey(editing.id, value) }}\n onSetDefault={() => { props.channels.setDefaultChannel(editing.id) }}\n onRemove={() => { props.channels.setChannels(channels.filter(channel => channel.id !== editing.id)); if (editing.id === state.channels.defaultChannelId && channels.length > 1) { const next = channels.find(channel => channel.id !== editing.id); if (next !== undefined) props.channels.setDefaultChannel(next.id) } setEditingId(null) }}\n onClose={() => { setEditingId(null) }}\n />\n ) : null}\n\n </li>\n )\n}\n\n/** Channel row + dialog helpers -------------------------------------------------- */\n\nfunction newChannelDraft(preset: PresetProviderView | undefined): ChannelDraft {\n return {\n id: clientId(),\n preset: preset?.id ?? '',\n name: preset?.name ?? '',\n apiUrl: preset?.apiUrl ?? '',\n apiUrlFull: false,\n models: (preset?.models ?? []).map(model => ({ ...model })),\n }\n}\n\nfunction addCustomChannel(channels: ChannelDraft[], form: ChannelsFormActions, openEditor: (id: string) => void): void {\n const draft = newChannelDraft(undefined)\n form.setChannels([...channels, draft])\n openEditor(draft.id)\n}\n\n/** Patch one field (or models) of one staged channel. */\nfunction replaceChannel(channels: ChannelDraft[], id: string, patch: Partial<ChannelDraft>, form: ChannelsFormActions): void {\n form.setChannels(channels.map(channel => channel.id === id ? { ...channel, ...patch } : channel))\n}\n\nfunction clientId(): string {\n const random = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : undefined\n return random ?? `ch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/** Built-in provider picker, expanded inside the settings card. */\nfunction PresetPicker(props: {\n t: (key: ImageGenKey, params?: Record<string, string | number>) => string\n presets: PresetProviderView[]\n error: string | null\n disabled: boolean\n onLoad: () => void\n onPick: (preset: PresetProviderView) => void\n onCustom: () => void\n onClose: () => void\n}) {\n const { t } = props\n const loadedRef = useRef(false)\n useEffect(() => {\n if (loadedRef.current) return\n loadedRef.current = true\n props.onLoad()\n }, [])\n return (\n <section className={css.presetInline} aria-label={t('channels.presetPickerTitle')}>\n <header className={css.presetInlineHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.presetPickerTitle')}</h3>\n <p className={css.sectionHint}>{t('channels.presetPickerHint')}</p>\n </div>\n <button type=\"button\" className={css.editorClose} aria-label={t('preview.close')} onClick={props.onClose}>×</button>\n </header>\n <div className={css.presetList}>\n {props.presets.map(preset => (\n <button key={preset.id} type=\"button\" className={css.presetRow} disabled={props.disabled} onClick={() => { props.onPick(preset) }}>\n <span className={css.presetName}>{preset.name}</span>\n <span className={css.presetMeta}>{preset.models.map(model => model.alias).join(' · ')}</span>\n </button>\n ))}\n <button type=\"button\" className={css.presetRow} data-custom disabled={props.disabled} onClick={props.onCustom}>\n <span className={css.presetName}>+ {t('channels.addCustom')}</span>\n <span className={css.presetHint}>{t('channels.presetCustomHint')}</span>\n </button>\n {props.error !== null ? <p className={css.failed} role=\"status\">{t('channels.presetLoadFailed', { error: props.error })}</p> : null}\n </div>\n </section>\n )\n}\n\n/** Channel editor (modal): key, display name, API URL, model catalog. */\nfunction ChannelEditor(props: {\n t: (key: ImageGenKey, params?: Record<string, string | number>) => string\n channel: ChannelDraft\n keyHeld: boolean\n otherChannels: ChannelDraft[]\n isDefault: boolean\n writable: boolean\n onPatch: (patch: Partial<ChannelDraft>) => void\n onSetModels: (models: ModelMapping[]) => void\n onSetKey: (value: string | undefined) => void\n onSetDefault: () => void\n onRemove: () => void\n onClose: () => void\n}) {\n const { t, channel } = props\n const [keyDraft, setKeyDraft] = useState('')\n const [candidates, setCandidates] = useState<string[] | null>(null)\n const [detecting, setDetecting] = useState(false)\n const [detectError, setDetectError] = useState<string | null>(null)\n const [manualId, setManualId] = useState('')\n const [removeOpen, setRemoveOpen] = useState(false)\n const [copyFrom, setCopyFrom] = useState('')\n\n const detect = (): void => {\n setDetecting(true)\n setDetectError(null)\n const payload: Record<string, unknown> = { channelId: channel.id }\n if (channel.apiUrl.trim() !== '') payload.apiUrl = channel.apiUrl.trim()\n if (keyDraft.trim() !== '') payload.apiKey = keyDraft.trim()\n void fetch(IMAGE_MODEL_API.models, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; models?: string[]; message?: string }\n if (!response.ok || body.ok !== true) throw new Error(body.message ?? `HTTP ${response.status}`)\n setCandidates(body.models ?? [])\n })\n .catch(error => { setDetectError(error instanceof Error ? error.message : String(error)) })\n .finally(() => { setDetecting(false) })\n }\n\n // Auto-detect once when the dialog opens with a complete endpoint.\n const autoDetected = useRef(false)\n useEffect(() => {\n if (autoDetected.current) return\n autoDetected.current = true\n if (channel.apiUrl.trim() !== '' && (props.keyHeld || keyDraft.trim() !== '')) detect()\n }, [])\n\n const addManual = (): void => {\n const id = manualId.trim()\n if (id === '') return\n const next = [...channel.models]\n const alias = id\n if (!next.some(model => model.alias === alias)) next.push({ alias, id })\n props.onSetModels(next)\n setManualId('')\n }\n\n const copyFromChannel = (): void => {\n const source = props.otherChannels.find(chance => chance.id === copyFrom)\n if (source === undefined) return\n const merged = [...channel.models]\n for (const model of source.models) {\n const alias = model.alias\n // Copy with a collision suffix so both sources stay selectable.\n let unique = alias\n let suffix = 2\n while (merged.some(entry => entry.alias === unique)) unique = `${alias} (${suffix++})`\n merged.push({ alias: unique, id: model.id })\n }\n props.onSetModels(merged)\n setCopyFrom('')\n }\n\n return (\n <div className={css.editorBackdrop} role=\"dialog\" aria-modal=\"true\" aria-label={`${t('channels.editorTitle')} · ${channel.name || t('channels.untitled')}`} onClick={props.onClose}>\n <div className={css.editorPanel} onClick={event => { event.stopPropagation() }}>\n <header className={css.editorHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.editorTitle')} · {channel.name || t('channels.untitled')}</h3>\n <p className={css.sectionHint}>{t('channels.editorSaveNote')}</p>\n </div>\n <button type=\"button\" className={css.editorClose} aria-label={t('preview.close')} onClick={props.onClose}>×</button>\n </header>\n\n <div className={css.editorField}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-name\">{t('channels.displayName')}</label>\n <input id=\"dsh-imagegen-channel-name\" className={css.input} value={channel.name} placeholder={t('channels.untitled')} disabled={!props.writable} onChange={event => { props.onPatch({ name: event.target.value }) }} />\n </div>\n <div className={css.editorField}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-url\">{t('channels.apiUrl')}</label>\n <input id=\"dsh-imagegen-channel-url\" className={css.input} value={channel.apiUrl} placeholder=\"https://api.example.com/v1\" disabled={!props.writable} onChange={event => { props.onPatch({ apiUrl: event.target.value }) }} />\n </div>\n <div className={css.editorField}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-key\">{t('channels.apiKey')}</label>\n {props.keyHeld || keyDraft !== ''\n ? (\n <button type=\"button\" className={css.reset} disabled={!props.writable} onClick={() => { setKeyDraft(''); props.onSetKey(undefined) }}>\n {t('channels.keyClear')}\n </button>\n )\n : null}\n </div>\n <input\n id=\"dsh-imagegen-channel-key\"\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={keyDraft}\n placeholder={props.keyHeld ? t('channels.keyReplaceHint') : t('channels.keyMissingHint')}\n disabled={!props.writable}\n onChange={event => { const value = event.target.value; setKeyDraft(value); props.onSetKey(value === '' ? undefined : value) }}\n />\n </div>\n\n <div className={css.editorDivider} />\n\n <div className={css.editorSectionHeader}>\n <h4 className={css.label}>{t('channels.modelCatalogTitle')}</h4>\n <button type=\"button\" className={css.modelFetch} disabled={!props.writable || detecting} onClick={detect}>\n {detecting ? t('channels.detecting') : t('channels.detect')}\n </button>\n </div>\n {detectError !== null ? <p className={css.failed} role=\"status\">{t('channels.detectFailed', { error: detectError })}</p> : null}\n {candidates !== null && detectError === null ? <p className={css.detectOk} role=\"status\">{t('channels.detectSuccess', { n: candidates.length })}</p> : null}\n\n {channel.models.length === 0\n ? <p className={css.sectionHint}>{t('channels.noModelsHint')}</p>\n : (\n <ul className={css.modelRows}>\n {channel.models.map((model, index) => {\n const entry = describeModel(model.id || model.alias)\n return (\n <li key={`${model.alias}-${index}`} className={css.modelRow}>\n <div className={css.modelRowInputs}>\n <input className={css.input} value={model.alias} aria-label={t('channels.modelAliasLabel')} disabled={!props.writable} onChange={event => {\n const next = [...channel.models]\n next[index] = { ...model, alias: event.target.value }\n props.onSetModels(next)\n }} />\n <span className={css.modelArrow}>→</span>\n <input className={css.input} value={model.id} aria-label={t('channels.modelIdLabel')} disabled={!props.writable} onChange={event => {\n const next = [...channel.models]\n next[index] = { ...model, id: event.target.value }\n props.onSetModels(next)\n }} />\n </div>\n <div className={css.modelRowBadges}>\n <span className={css.modelBadge}>{entry.labelZh}{entry.known ? '' : ` · ${t('channels.unknownProtocol')}`}</span>\n <button type=\"button\" className={css.modelRowRemove} disabled={!props.writable} aria-label={`${t('channels.removeModel')}: ${model.alias}`} onClick={() => { props.onSetModels(channel.models.filter((_, i) => i !== index)) }}>×</button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n\n <div className={css.editorTools}>\n <div className={css.manualModelRow}>\n <input className={css.input} value={manualId} placeholder={t('channels.manualAddPlaceholder')} disabled={!props.writable} onChange={event => { setManualId(event.target.value) }} onKeyDown={event => { if (event.key === 'Enter') { event.preventDefault(); addManual() } }} />\n <button type=\"button\" className={css.addModel} disabled={!props.writable || manualId.trim() === ''} onClick={addManual}>{t('channels.addModelConfirm')}</button>\n </div>\n {props.otherChannels.length > 0 ? (\n <div className={css.manualModelRow}>\n <select className={css.modelChoices} value={copyFrom} disabled={!props.writable} onChange={event => { setCopyFrom(event.target.value) }} aria-label={t('channels.copyFrom')}>\n <option value=\"\">{t('channels.copyFrom')}</option>\n {props.otherChannels.map(other => (\n <option key={other.id} value={other.id}>{other.name || t('channels.untitled')}</option>\n ))}\n </select>\n <button type=\"button\" className={css.addModel} disabled={!props.writable || copyFrom === ''} onClick={copyFromChannel}>{t('channels.copyApply')}</button>\n </div>\n ) : null}\n </div>\n\n {candidates !== null && candidates.length > 0 ? (\n <div className={css.modelCandidateList}>\n <span className={css.modelCandidateLabel}>\n {t('channels.candidatesTitle')}\n </span>\n {candidates.map(candidate => {\n const selected = channel.models.some(model => model.alias === candidate)\n const entry = describeModel(candidate)\n return (\n <label key={candidate} className={css.modelCandidate} data-selected={selected ? '' : undefined}>\n <input type=\"checkbox\" checked={selected} disabled={!props.writable} onChange={() => {\n const merged = selected\n ? channel.models.filter(model => model.alias !== candidate)\n : [...channel.models, { alias: candidate, id: candidate }]\n props.onSetModels(merged)\n }} />\n <span>{candidate}</span>\n {!entry.known ? <span className={css.modelBadge} data-warn>{t('channels.unknownProtocol')}</span> : <span className={css.modelBadge}>{entry.labelZh}</span>}\n </label>\n )\n })}\n </div>\n ) : null}\n\n <div className={css.editorDivider} />\n\n <div className={css.editorFooter}>\n {props.isDefault ? <span className={css.channelBadge} data-default>{t('channels.defaultLabel')}</span> : (\n <button type=\"button\" className={css.inlineDisclosure} disabled={!props.writable} onClick={props.onSetDefault}>{t('channels.setDefault')}</button>\n )}\n <span className={css.spacer} />\n {removeOpen\n ? (\n <>\n <button type=\"button\" className={css.channelDanger} disabled={!props.writable} onClick={props.onRemove}>{t('channels.confirm')}</button>\n <button type=\"button\" className={css.channelAction} onClick={() => { setRemoveOpen(false) }}>{t('channels.cancel')}</button>\n </>\n )\n : (\n <button type=\"button\" className={css.channelAction} data-danger onClick={() => { setRemoveOpen(true) }}>{t('channels.deleteThisChannel')}</button>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\ninterface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field; `secret` renders a password control. */\nfunction ValueField(props: FieldProps & {\n /** Render a password control. */\n secret?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n /** Label of the dedicated clear control (secret fields). */\n clearLabel?: string\n /** Stage a clear of the stored secret. */\n onClear?: () => void\n /** Whether a stored secret exists (enables the clear control). */\n canClear?: boolean\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n {props.secret === true && props.canClear === true\n ? (\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onClear}\n >\n {props.clearLabel ?? props.resetLabel}\n </button>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type={props.secret === true ? 'password' : 'text'}\n autoComplete={props.secret === true ? 'off' : undefined}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nfunction BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <select\n id={props.id}\n className={css.select}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n <option value=\"\">{props.inheritLabel}</option>\n <option value=\"true\">{props.onLabel}</option>\n <option value=\"false\">{props.offLabel}</option>\n </select>\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n","/**\n * Browser-side settings scope for the dsh-imagegen namespace, served by the\n * plugin's own loopback bridge routes (/api/dsh-imagegen/settings). The\n * official rc.6 settings scope answers \"unavailable\" for every third-party\n * namespace (the host-apiproxy allowlist is hard-coded), so this package\n * re-serves its namespace through the host settings seam over a same-origin,\n * loopback-only HTTP pair — the same pattern the dsh-web-ui family bridge\n * uses, self-contained per plugin.\n */\n\nimport {\n createSnapshotStore,\n type SnapshotStore,\n} from '@deepseek-ai/dsh-client-store'\nimport type { SettingsPathOpView } from '@deepseek-ai/dsh-api-remotes/client'\nimport { IMAGEGEN_SETTINGS_NAMESPACE, SETTINGS_API, type ChannelConfig } from '../protocol.ts'\n\n/**\n * Client-side sync state of one settings namespace, owned here because this\n * package re-serves its namespace over its own bridge rather than consuming\n * the host scope service, and the host generation this bridge was written\n * against no longer exports the contract.\n */\nexport interface SettingsScopeSnapshot<T> {\n status: 'loading' | 'ready' | 'unavailable'\n value: T | undefined\n base: unknown\n user: unknown\n revision: number | undefined\n writable: boolean\n mode: 'host' | 'memory'\n}\n\n/** Reactive owner handle over one namespace's durable section. */\nexport interface SettingsScope<T> {\n getSnapshot(): SettingsScopeSnapshot<T>\n subscribe(listener: () => void): () => void\n mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<void>\n set(field: string, value: unknown): Promise<void>\n unset(field: string): Promise<void>\n}\n\n/** One cascading-picker group: a configured channel and its model aliases. */\nexport interface ImageModelGroup {\n /** Stable channel id; '' is the legacy single-channel fallback. */\n id: string\n /** Display name shown in the cascading model picker. */\n name: string\n /** Model aliases configured on this channel, in display order. */\n models: string[]\n}\n\n/** The fields this plugin's settings card edits. */\nexport interface ImageGenConfig {\n enabled?: boolean\n announceToAgent?: boolean\n allowAgentImageGeneration?: boolean\n /** Configured channels (each: name, endpoint, model catalog). */\n channels?: ChannelConfig[]\n /** Per-channel API keys, keyed by channel id. The redacted wire view returns\n * this as an empty object; key presence comes from the secrets sidecar. */\n channelSecrets?: Record<string, string>\n /** Channel used when a request does not name one. */\n defaultChannelId?: string\n /** Model alias selected by default in the GUI. Empty uses the first option. */\n defaultModel?: string\n promptApiUrl?: string\n promptApiKey?: string\n promptModel?: string\n /* ----- deprecated legacy single-endpoint fields (migrated to channels) ----- */\n apiUrl?: string\n apiKey?: string\n imageModels?: string[]\n}\n\n/** One settings path-op as the bridge consumes it. */\nexport type SettingsOp = { op: 'set'; path: string[]; value: unknown } | { op: 'unset'; path: string[] }\n\n/** Wire shape of one namespace view from the bridge. */\ninterface BridgeView {\n ns: string\n value: unknown\n base?: unknown\n user?: unknown\n revision: number\n secrets?: Array<{ path: string[]; set: boolean }>\n}\n\n/** The bridge response envelope ({ ok: true, value } | { ok: false, code, message }). */\ntype BridgeEnvelope =\n | { ok: true; value: { namespaces?: BridgeView[]; writable?: boolean } | BridgeView }\n | { ok: false; code: string; message: string }\n\n/** Settings wire face over the bridge routes (fetch-backed). */\nfunction createBridgeApi(fetchFn: typeof fetch): {\n settings: {\n describe(payload: Record<string, never>): Promise<{ result: BridgeEnvelope }>\n mutate(payload: { ns: string; ops: unknown[]; expectedRevision?: number }): Promise<{ result: BridgeEnvelope }>\n }\n} {\n const post = async (path: string, body: unknown): Promise<{ result: BridgeEnvelope }> => {\n try {\n const response = await fetchFn(path, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n return { result: { ok: false, code: 'internal', message: `bridge HTTP ${response.status}` } }\n }\n return { result: await response.json() as BridgeEnvelope }\n } catch {\n return { result: { ok: false, code: 'internal', message: 'settings bridge unreachable' } }\n }\n }\n return {\n settings: {\n describe: async payload => post(SETTINGS_API.describe, payload),\n mutate: async payload => post(SETTINGS_API.mutate, payload),\n },\n }\n}\n\n/**\n * A SettingsScope over the bridge face: serialized queue, revision-fenced\n * writes, recovery read after a refusal. Mirrors the official controller's\n * ordering but trusts the Host-seam value without re-running the wire-schema\n * validation — the seam already validated it.\n */\nclass BridgeScopeController<T> implements SettingsScope<T> {\n private readonly store: SnapshotStore<SettingsScopeSnapshot<T>>\n /** Whether the namespace currently holds a stored secret (e.g. apiKey). */\n private readonly keySet: SnapshotStore<boolean>\n /** Individual secret presence bits, keyed by the settings field name. */\n private readonly secretSets: SnapshotStore<Record<string, boolean>>\n private tail: Promise<void> = Promise.resolve()\n private disposed = false\n\n constructor(\n private readonly api: ReturnType<typeof createBridgeApi>['settings'],\n private readonly spec: { namespace: string },\n ) {\n this.store = createSnapshotStore<SettingsScopeSnapshot<T>>({\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n })\n this.keySet = createSnapshotStore(false)\n this.secretSets = createSnapshotStore({})\n }\n\n getSnapshot(): SettingsScopeSnapshot<T> {\n return this.store.getSnapshot()\n }\n\n /** Whether a stored secret exists (from the redacted view's secrets list). */\n getKeySetSnapshot(): boolean {\n return this.keySet.getSnapshot()\n }\n\n /** Observe the secret-set flag. */\n subscribeKeySet(listener: () => void): () => void {\n return this.keySet.subscribe(listener)\n }\n\n /** Whether a specific secret field currently has a stored value. */\n getSecretSetSnapshot(field: string): boolean {\n return this.secretSets.getSnapshot()[field] === true\n }\n\n /** Observe changes to individual secret-field presence bits. */\n subscribeSecretSets(listener: () => void): () => void {\n return this.secretSets.subscribe(listener)\n }\n\n subscribe(listener: () => void): () => void {\n return this.store.subscribe(listener)\n }\n\n /** Queue a bridge refresh. */\n load(): Promise<void> {\n return this.enqueue(() => this.read())\n }\n\n set(field: string, value: unknown): Promise<void> {\n return this.enqueue(() => this.writeOps([{ op: 'set', path: [field], value }]))\n }\n\n unset(field: string): Promise<void> {\n return this.enqueue(() => this.writeOps([{ op: 'unset', path: [field] }]))\n }\n\n mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<void> {\n return this.enqueue(() => this.writeOps([...ops] as SettingsOp[], expectedRevision))\n }\n\n /** Apply several path ops in one revision-fenced mutate call (atomic save).\n * Path ops may address plain-object fields (e.g. `channelSecrets.<id>`),\n * but never navigate *inside* arrays — write array fields wholesale. */\n mutateOps(ops: SettingsOp[]): Promise<void> {\n return this.enqueue(() => this.writeOps(ops))\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n await this.tail\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n if (this.disposed) return Promise.resolve()\n const task = this.tail.then(async () => {\n if (this.disposed) return\n await operation()\n })\n this.tail = task.catch(() => {})\n return task\n }\n\n private async read(): Promise<void> {\n let response\n try {\n response = await this.api.describe({})\n } catch {\n if (!this.disposed) this.store.update(draft => { draft.status = 'unavailable' })\n return\n }\n if (!response.result.ok || this.disposed) {\n if (!this.disposed) this.store.update(draft => { draft.status = 'unavailable' })\n return\n }\n const { namespaces, writable } = response.result.value as { namespaces?: BridgeView[]; writable?: boolean }\n const view = namespaces?.find(candidate => candidate.ns === this.spec.namespace)\n if (view === undefined) {\n this.store.update(draft => {\n draft.status = 'unavailable'\n draft.writable = writable === true\n })\n this.keySet.set(false)\n this.secretSets.set({})\n return\n }\n this.accept(view, writable)\n }\n\n private async writeOps(ops: SettingsOp[], expectedRevision?: number): Promise<void> {\n const revision = expectedRevision ?? this.getSnapshot().revision\n let response\n try {\n response = await this.api.mutate({\n ns: this.spec.namespace,\n ops,\n ...revision === undefined ? {} : { expectedRevision: revision },\n })\n } catch {\n await this.read()\n return\n }\n if (!response.result.ok || this.disposed) {\n await this.read()\n return\n }\n this.accept(response.result.value as BridgeView, undefined)\n }\n\n private accept(view: BridgeView, writable: boolean | undefined): void {\n this.store.update(draft => {\n draft.revision = view.revision\n draft.base = view.base\n draft.user = view.user\n if (writable !== undefined) draft.writable = writable\n draft.status = 'ready'\n // Trust the Host-seam value: the seam already validated it, and the\n // card binds without a narrowing decoder.\n draft.value = view.value as T\n })\n const secretSets = Object.fromEntries((view.secrets ?? []).map(secret => [secret.path.join('.'), secret.set]))\n this.keySet.set(Object.values(secretSets).some(Boolean))\n this.secretSets.set(secretSets)\n }\n}\n\n/** The bound scope plus the secret-set flag, as the card and panel consume it. */\nexport interface ImageGenScope extends SettingsScope<ImageGenConfig> {\n /** Queue a bridge refresh (the invalidation path re-reads the namespace). */\n load(): Promise<void>\n /** Apply several path ops in one revision-fenced mutate call. */\n mutateOps(ops: SettingsOp[]): Promise<void>\n getKeySetSnapshot(): boolean\n subscribeKeySet(listener: () => void): () => void\n getSecretSetSnapshot(field: string): boolean\n subscribeSecretSets(listener: () => void): () => void\n}\n\n/**\n * Bind the dsh-imagegen settings scope over the bridge routes and start its\n * initial read (the caller mounts nothing until the scope settles).\n * @param fetchFn - the fetch implementation (the global fetch on loopback).\n * @returns the scope; unavailable when the bridge is unreachable.\n */\nexport function bindImageGenScope(fetchFn: typeof fetch = fetch): ImageGenScope {\n const controller = new BridgeScopeController<ImageGenConfig>(createBridgeApi(fetchFn).settings, {\n // The host keys a plugin's settings descriptor by its loader entry id,\n // which is the host half's `export const name` — the same value the\n // bundle patch row uses.\n namespace: IMAGEGEN_SETTINGS_NAMESPACE,\n })\n void controller.load()\n return controller\n}\n\n/**\n * Project the configured channels into cascading model-picker groups. The\n * flat `models` list remains for history filters and compatibility, while\n * `groups` preserves which channel owns each alias (aliases may repeat across\n * channels). Falls back to the legacy flat allow-list while no channels exist.\n * Pure projection — no host calls.\n */\nexport function imageModelOptions(config: ImageGenConfig | undefined): {\n models: string[]\n groups: ImageModelGroup[]\n defaultChannelId?: string\n defaultModel?: string\n} {\n const channels = config?.channels ?? []\n if (channels.length === 0) {\n const legacy = Array.isArray(config?.imageModels)\n ? config.imageModels.filter((model): model is string => typeof model === 'string' && model.trim() !== '')\n : []\n const defaultModel = typeof config?.defaultModel === 'string' && legacy.includes(config.defaultModel)\n ? config.defaultModel\n : undefined\n const ordered = defaultModel === undefined ? legacy : [defaultModel, ...legacy.filter(model => model !== defaultModel)]\n return {\n models: ordered,\n groups: ordered.length === 0 ? [] : [{ id: '', name: '', models: ordered }],\n ...defaultModel === undefined ? {} : { defaultModel },\n }\n }\n\n const defaultId = config?.defaultChannelId !== undefined && channels.some(channel => channel.id === config.defaultChannelId)\n ? config.defaultChannelId\n : channels[0]!.id\n const orderedChannels = [\n channels.find(channel => channel.id === defaultId)!,\n ...channels.filter(channel => channel.id !== defaultId),\n ]\n const groups: ImageModelGroup[] = orderedChannels.flatMap(channel => {\n const models = channel.models\n .map(model => model.alias.trim())\n .filter((alias, index, all) => alias !== '' && all.indexOf(alias) === index)\n return models.length === 0 ? [] : [{ id: channel.id, name: channel.name.trim() || channel.id, models }]\n })\n\n const aliases: string[] = []\n for (const group of groups) {\n for (const alias of group.models) {\n if (!aliases.includes(alias)) aliases.push(alias)\n }\n }\n const defaultModel = typeof config?.defaultModel === 'string' && aliases.includes(config.defaultModel)\n ? config.defaultModel\n : undefined\n const models = defaultModel === undefined ? aliases : [defaultModel, ...aliases.filter(model => model !== defaultModel)]\n return {\n models,\n groups,\n defaultChannelId: defaultId,\n ...defaultModel === undefined ? {} : { defaultModel },\n }\n}\n","/** Inline renderer for image-generation tool-result attachments. */\n\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport { useEffect, useMemo, useState } from 'react'\nimport { AGENT_IMAGE_API } from '../protocol.ts'\nimport css from './image-toolview.module.css'\n\n/** Owner props supplied by the host's keyed tool-call slot. */\nexport interface ImageToolViewOwnerProps {\n callId: string\n toolName: string\n block: ToolCallBlock\n cwd?: string\n home?: string\n openFile: (path: string) => void\n inspect?: () => void\n}\n\ninterface LoadedImage {\n ref: ImageAttachmentRef\n src: string\n}\n\ninterface ImageToolViewProps extends ImageToolViewOwnerProps {\n sessionId: SessionId\n}\n\nfunction isSettled(block: ToolCallBlock): block is Extract<ToolCallBlock, { kind: 'tool-result' }> {\n return 'kind' in block\n}\n\nfunction imageRefsOf(block: ToolCallBlock): ImageAttachmentRef[] {\n if (!isSettled(block)) return []\n return block.content\n .flatMap(content => content.type === 'image' ? [content.attachment] : [])\n}\n\nfunction textOf(block: ToolCallBlock): string {\n if (!isSettled(block)) return ''\n return block.content\n .filter(content => content.type === 'text')\n .map(content => content.text)\n .join('\\n')\n}\n\nfunction resultInfo(block: ToolCallBlock): { status: string; message: string } {\n if (!isSettled(block)) return { status: 'running', message: '正在生成图片…' }\n const text = textOf(block)\n try {\n const parsed = JSON.parse(text) as { status?: unknown; message?: unknown }\n return {\n status: typeof parsed.status === 'string' ? parsed.status : block.isError ? 'failed' : 'completed',\n message: typeof parsed.message === 'string' ? parsed.message : '',\n }\n } catch {\n return { status: block.isError ? 'failed' : 'completed', message: text }\n }\n}\n\nfunction statusLabel(status: string): string {\n if (status === 'running' || status === 'queued') return '生成中'\n if (status === 'failed') return '生成失败'\n if (status === 'cancelled') return '已取消'\n return '图片结果'\n}\n\nfunction useAttachmentImages(\n sessionId: SessionId,\n refs: ImageAttachmentRef[],\n load: (sessionId: SessionId, ref: ImageAttachmentRef) => Promise<string>,\n): { images: LoadedImage[]; error: string | null } {\n const key = useMemo(() => refs.map(ref => String(ref.attachmentId)).join('|'), [refs])\n const [images, setImages] = useState<LoadedImage[]>([])\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let disposed = false\n const urls: string[] = []\n const revoke = (): void => {\n for (const url of urls) URL.revokeObjectURL(url)\n urls.length = 0\n }\n\n setImages([])\n setError(null)\n if (refs.length === 0) return () => { /* no attachments to clean up */ }\n\n void Promise.all(refs.map(async ref => {\n const src = await load(sessionId, ref)\n urls.push(src)\n return { ref, src }\n }))\n .then(next => {\n if (!disposed) setImages(next)\n })\n .catch(errorValue => {\n revoke()\n if (!disposed) setError(errorValue instanceof Error ? errorValue.message : String(errorValue))\n })\n\n return () => {\n disposed = true\n revoke()\n }\n }, [key, load, refs, sessionId])\n\n return { images, error }\n}\n\n/** Register the inline image result view for all image-generation result tools. */\nexport function registerImageToolviews(ctx: ClientContext): void {\n const load = async (_sessionId: SessionId, ref: ImageAttachmentRef): Promise<string> => {\n // Tool-result images intentionally do not occur in model-visible session\n // content, so session.readAttachment() rejects them. The plugin route\n // reads the same durable attachment after validating the complete ref.\n const query = new URLSearchParams({\n attachment_id: String(ref.attachmentId),\n media_type: ref.mediaType,\n bytes: String(ref.bytes),\n width: String(ref.width),\n height: String(ref.height),\n })\n const response = await fetch(`${AGENT_IMAGE_API}?${query.toString()}`)\n if (!response.ok) throw new Error(`无法读取图片附件(HTTP ${response.status})。`)\n const blob = await response.blob()\n return URL.createObjectURL(blob)\n }\n\n const ImageToolView = (props: ImageToolViewProps): React.JSX.Element => {\n const refs = useMemo(() => imageRefsOf(props.block), [props.block])\n const { status, message } = resultInfo(props.block)\n const { images, error } = useAttachmentImages(props.sessionId, refs, load)\n\n return <section className={css.root} data-state={status} data-tool={props.toolName}>\n <header className={css.header}>\n <span className={css.icon} aria-hidden=\"true\">▧</span>\n <strong>{props.toolName}</strong>\n <span className={css.status}>{statusLabel(status)}</span>\n </header>\n {message !== '' && <p className={css.message}>{message}</p>}\n {images.length > 0 && <div className={css.images}>\n {images.map(image => <a\n className={css.imageLink}\n href={image.src}\n key={String(image.ref.attachmentId)}\n rel=\"noreferrer\"\n target=\"_blank\"\n title=\"打开原图\"\n >\n <img className={css.image} src={image.src} alt={image.ref.name ?? '生成图片'} />\n </a>)}\n </div>}\n {refs.length > 0 && images.length === 0 && error === null && <p className={css.loading}>正在加载图片…</p>}\n {error !== null && <p className={css.error}>{error}</p>}\n </section>\n }\n\n ctx.slots.inject('tool.call.toolview', function* () {\n for (const key of ['generate_image', 'edit_image', 'get_image_generation_task']) {\n yield ctx.slots.register({\n name: 'tool.call.toolview',\n key,\n inject: (sessionId: string) => ({ sessionId: sessionId as SessionId }),\n }, ImageToolView)\n }\n })\n}\n","/**\n * Browser-half entry for the dsh-imagegen plugin — runs inside the dsh web GUI.\n *\n * Registers the locale dictionaries, binds the plugin's own settings scope (its\n * bridge routes serve the namespace), registers the row's configuration entry\n * onto the Plugins page's per-row configuration slot, and registers the inline\n * image renderer for image-generation tool results. Failure policy: mounting\n * problems are logged, never thrown — the web shell fails the whole boot when a\n * plugin apply throws, and an external plugin must not take the GUI down.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: pulls the LocaleNamespaceMap merge table.\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { applyHostLocale } from './helpers.ts'\nimport { en, ru, zh, type ImageGenKey } from './locales.ts'\nimport { ImageGenSettingsCard, ImageGenSettingsCardController } from './SettingsCard.tsx'\nimport { bindImageGenScope, type ImageGenScope } from './settings-scope.ts'\nimport { registerImageToolviews, type ImageToolViewOwnerProps } from './image-toolview.tsx'\n\n/** Locale namespace this plugin owns. */\nconst NS = 'dsh-imagegen'\n\n/**\n * The patch row this plugin's own `cordis.patch.yml` inserts.\n *\n * It equals the host half's `export const name` in `src/index.ts`, and the\n * browser half addresses its configuration entry by\n * `<bundle package name>#<this id>`.\n */\nexport const IMAGEGEN_ROW_ID = 'imagegen'\n\n/**\n * Bundle package names whose row {@link IMAGEGEN_ROW_ID} this card configures.\n *\n * The Plugins page keys a row's configuration by the name of the package that\n * declares the row. This plugin reaches a profile in one of two shapes, and the\n * key differs between them: as a dependency of this repository's aggregate\n * bundle, the declaring package is the aggregate; installed on its own, it is\n * this package. Both keys are registered — the one whose bundle is not\n * installed simply never renders, because the page dispatches only the keys its\n * own bundles declare.\n */\nexport const IMAGEGEN_BUNDLE_NAMES = [\n '@logictan/dsh-plugins-all',\n '@logictan/dsh-imagegen',\n] as const\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-imagegen surface copy. */\n 'dsh-imagegen': ImageGenKey\n }\n\n interface SlotMap {\n /**\n * The official plugin row-configuration slot the Plugins page declares for\n * a row a bundle contributes (the configure control beside the row opens\n * this entry's own page). This card registers there as its own standalone\n * entry — independent of the dsh-web-ui family group — so this plugin never\n * reads as part of that family. Spelled here with the same shape so this\n * package can register without depending on the sibling UI package.\n */\n 'plugins.row.config': { kind: 'keyed'; scope: 'root'; owner: ImageGenPluginConfigOwnerProps }\n /** Image-generation results render their durable image blocks inline. */\n 'tool.call.toolview': { kind: 'keyed'; scope: 'session'; owner: ImageToolViewOwnerProps }\n }\n}\n\n/** Owner share of a plugin row's configuration entry. */\nexport interface ImageGenPluginConfigOwnerProps {\n /** The view the Plugins page asks for: the row's one-liner, or its page. */\n readonly view: 'summary' | 'page'\n}\n\n/** Required services (fiber inject waiting — the runtime must be up first). */\nexport const inject = ['slots', 'locale', 'connection']\n\n/**\n * Mount the settings card and the inline image renderer.\n * @param ctx - client root context (services: slots, locale, connection).\n */\nexport function apply(ctx: ClientContext): void {\n // The host locale service only knows zh/en dictionaries (its type is\n // fixed); ru rides the untyped single-locale registration instead.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-imagegen: dictionaries')\n // Russian ships as a language pack: the dictionary lands in this plugin's\n // namespace, and Русский joins the shared DSH language catalog (Settings →\n // General → Language) with per-key fallback to English for host copy.\n // Registration order/aggregation between register(NS, 'ru', …) and\n // addLanguage differs across host builds and a duplicate throws — these\n // surfaces must degrade silently, never fail the GUI boot.\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, 'ru', ru)\n } catch (error) {\n console.warn('[dsh-imagegen] ru dictionary not registered:', error)\n return () => {}\n }\n }, 'dsh-imagegen: ru dictionary')\n ctx.effect(() => {\n try {\n if (ctx.locale.getLocale().locales.some(locale => locale.id === 'ru')) return () => {}\n return ctx.locale.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })\n } catch (error) {\n console.warn('[dsh-imagegen] ru language not added to the catalog:', error)\n return () => {}\n }\n }, 'dsh-imagegen: ru language pack')\n // Every plugin surface renders through tt(); bridge DSH locale switches\n // into it so the whole plugin follows the interface language.\n ctx.effect(() => {\n const applyLocale = (): void => { applyHostLocale(ctx.locale.getLocale().active) }\n applyLocale()\n return ctx.locale.subscribe(applyLocale)\n }, 'dsh-imagegen: follow host locale')\n registerImageToolviews(ctx)\n\n const connection = ctx.get('connection') as ConnectionHandle | undefined\n const loopback = connection?.isLoopback === true\n // The bridge routes are loopback-fenced; remote browsers get an unavailable\n // scope (the card explains the gap) instead of failing fetches.\n const scope: ImageGenScope = bindImageGenScope(loopback\n ? (input, init) => fetch(input, init)\n : () => { throw new Error('settings bridge is loopback-only') })\n\n // Re-read the scope whenever the connection resets (same invalidation the\n // official settings binder wires).\n ctx.effect(() => {\n const disposers = [\n ctx.on('connection/reset', () => { void scope.load() }),\n ]\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-imagegen: settings scope invalidation')\n\n // Plugin configuration entry: one staged form over the plugin's settings\n // scope, registered onto the Plugins page's per-row configuration slot. One\n // entry per bundle that can declare this row; the slot is key-dispatched by\n // `<bundle>#<row id>`, so both keys are registered and the bundle that is not\n // installed simply never dispatches its key.\n const settingsCard = new ImageGenSettingsCardController(scope)\n for (const bundle of IMAGEGEN_BUNDLE_NAMES) {\n ctx.slots.inject('plugins.row.config', () => ctx.slots.register({\n name: 'plugins.row.config',\n key: `${bundle}#${IMAGEGEN_ROW_ID}`,\n locale: NS,\n inject: () => settingsCard.inject(),\n }, ImageGenSettingsCard))\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["css","css"],"sources":["../src/client/locales.ts","../src/locale-tables.ts","../src/client/helpers.ts","../src/client/settings-form.ts","../src/client/channels-form.ts","../src/model-catalog.ts","../src/protocol.ts","../src/client/use-language.ts","../src/client/SettingsCard.tsx","../src/client/settings-scope.ts","../src/client/image-toolview.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * dsh-imagegen surface copy: zh is the key source, en mirrors every key.\n */\n\nexport const zh = {\n 'entry.label': 'AI 生图',\n 'entry.tooltip': 'AI 生图面板(gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream 系列)',\n 'entry.newSession': '新会话',\n 'entry.newSessionTooltip': '创建一个新的 DSH 会话',\n 'entry.image': '生图',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': '觉得好用或有建议?欢迎来 GitHub 提 issues、点个 star 支持一下!',\n 'conversation.add': '加入对话',\n 'conversation.adding': '加入中…',\n 'conversation.added': '图片已加入当前对话',\n 'conversation.addHint': '加入对话后可使用 /edit_image 修改;命令使用插件图片模型',\n 'conversation.noSession': '请先打开一个会话,再加入图片',\n 'conversation.unavailable': '当前会话暂不可用',\n 'conversation.busy': '当前对话正在发送,请稍后再试',\n // mode\n 'mode.text': '文生图',\n 'mode.edit': '图生图',\n 'workspace.label': '创作模式',\n 'workspace.normal': '普通生图',\n 'workspace.canvas': '无限画布',\n 'workspace.ecommerce': '电商模式',\n 'canvas.addImage': '添加图片',\n 'canvas.addText': '添加文本',\n 'canvas.generate': '生成图片',\n 'canvas.addToCanvas': '加入画布',\n 'canvas.untitled': '未命名画布',\n 'canvas.imageNode': '图片节点',\n 'canvas.textNode': '文本节点',\n 'canvas.fromGallery': '素材库图片',\n 'canvas.fromHistory': '历史图片',\n 'canvas.project': '画布项目',\n 'canvas.newCanvas': '新建画布',\n 'canvas.deleteCanvas': '删除画布',\n 'canvas.deleteCanvasConfirm': '再点一次确认删除',\n 'canvas.rename': '重命名画布',\n 'canvas.renameHint': '双击重命名',\n 'canvas.background': '画布背景',\n 'canvas.backgroundDots': '点阵',\n 'canvas.backgroundLines': '网格线',\n 'canvas.backgroundWaves': '波浪线',\n 'canvas.backgroundShapeGrid': '滚动方格',\n 'canvas.backgroundDotField': '点场',\n 'canvas.backgroundDotGrid': '互动点阵',\n 'canvas.backgroundFloatingLines': '浮动线条',\n 'canvas.backgroundFlow': '流体扰动',\n 'canvas.backgroundLiquid': '液态以太',\n 'canvas.backgroundFaultyTerminal': '故障终端',\n 'canvas.backgroundSilk': '丝绸',\n 'canvas.backgroundGalaxy': '星系',\n 'canvas.backgroundBlank': '空白',\n 'canvas.backgroundUpload': '上传自定义背景…',\n 'canvas.backgroundRemove': '移除自定义背景',\n 'canvas.templateLibrary': '模板库(选择提示词生成文本+配置节点)',\n 'canvas.undo': '撤销 (Ctrl+Z)',\n 'canvas.redo': '重做 (Ctrl+Shift+Z)',\n 'canvas.saving': '保存中…',\n 'canvas.saved': '已保存',\n 'canvas.saveFailed': '保存失败',\n 'canvas.loading': '加载中…',\n 'canvas.toolSelect': '选择 / 移动节点',\n 'canvas.toolPan': '拖动画布(空格 / Ctrl + 拖动)',\n 'canvas.delete': '删除',\n 'canvas.duplicate': '创建副本 (Ctrl+D)',\n 'canvas.download': '下载图片',\n 'canvas.fitView': '适应全部内容',\n 'canvas.minimapOpen': '打开缩略图',\n 'canvas.minimapClose': '关闭缩略图',\n 'canvas.minimap': '画布缩略图',\n 'canvas.zoom': '缩放',\n 'canvas.textPlaceholder': '输入文本内容…',\n 'canvas.generatingNode': '生成中…',\n 'canvas.generateFailed': '生成失败',\n 'canvas.retry': '重试',\n 'canvas.taskLost': '生成任务已丢失(服务重启),请重试',\n 'canvas.emptyImageNode': '点击上传图片',\n 'canvas.connectHint': '拖到其他节点建立连线',\n 'canvas.connectAddHint': '拖到其他节点建立连线;点击直接添加下一节点',\n 'canvas.resizeHint': '拖动调整大小(图片锁定比例)',\n 'canvas.count': '生成数量',\n 'canvas.configNode': '生成配置',\n 'canvas.addConfigNode': '添加配置节点',\n 'canvas.configHint': '把图片节点连线到左侧作为参考图,文本节点连线过来作为提示词;选中本节点后在下方输入或改写提示词并生成。',\n 'canvas.composerLinked': '已连接 {count} 个输入节点',\n 'canvas.composerPlaceholder': '描述要生成的图片;也可把文本节点连线过来作为提示词…',\n 'canvas.countUnit': '{count} 张',\n 'canvas.model': '生图模型',\n 'canvas.modelPlaceholder': '选择模型',\n 'canvas.size': '尺寸比例',\n 'canvas.sizeAuto': '自动比例',\n 'canvas.quality': '清晰度',\n 'canvas.qualityAuto': '自动清晰度',\n 'canvas.needApi': '请先配置生图 API',\n 'canvas.needPrompt': '请输入生成描述',\n 'canvas.needModel': '尚未配置可用生图模型',\n 'canvas.openSettings': '打开设置',\n 'canvas.deleteConnection': '删除连线',\n 'canvas.paste': '粘贴 (Ctrl+V)',\n 'canvas.addImageNode': '添加图片节点',\n 'canvas.addTextNode': '添加文本节点',\n 'canvas.addSketch': '添加画板(手绘草图生图)',\n 'canvas.sketchNode': '画板节点',\n 'canvas.addSketchNode': '添加画板节点',\n 'canvas.sketchPlaceholder': '在此自由绘制草图,连线到配置节点即可作为参考图生成',\n 'canvas.sketchBrush': '画笔',\n 'canvas.sketchEraser': '橡皮擦',\n 'canvas.sketchUndo': '撤销上一笔',\n 'canvas.sketchClear': '清空画板',\n 'canvas.sketchWidth': '笔触粗细',\n 'canvas.dismiss': '关闭提示',\n 'canvas.annotate': '标注:在图片上画框,自动挂上提示词卡片',\n 'canvas.annotationNode': '标注提示词',\n 'canvas.annotationTag': '标注',\n 'canvas.annotationPlaceholder': '描述这个框里要改成什么,例如:把这里换成一只橘猫',\n 'canvas.annotationDrawHint': '在图片上拖拽画框;松开后从框上拉出引导线并挂一张提示词卡片(跟随图片移动,无需连线)',\n 'canvas.removeBackground': '移除背景(本地抠图,输出透明 PNG)',\n 'canvas.removingBackground': '正在移除背景…',\n 'canvas.removeBgDone': '已移除背景(透明像素 {percent}%)',\n 'canvas.removeBgFailed': '背景太复杂,本地抠图没有找到可移除的背景;可以先用「标注」框选主体再让模型重绘',\n 'canvas.alreadyTransparent': '这张图片已经是透明背景了',\n 'canvas.noBackgroundNode': '去背景图片',\n 'canvas.splitLayers': '图层拆分(AI 拆成可编辑图层)',\n 'canvas.splittingLayers': '正在拆分图层…',\n 'canvas.layerSplitDone': '已拆分为 {count} 个图层节点',\n 'canvas.layerSplitEmpty': '模型没有识别出可拆分的图层',\n 'canvas.layerBackground': '背景层 · {label}',\n 'canvas.layerObject': '图层 · {label}',\n 'canvas.layerText': '文字层 · {label}',\n 'canvas.layerKindBackground': '背景',\n 'canvas.layerKindObject': '元素',\n 'canvas.layerKindText': '文字',\n 'canvas.nodeModel': '本节点使用的模型(重试与相连配置节点默认使用)',\n 'canvas.modelDefault': '默认模型',\n 'canvas.fontSmaller': '缩小字号',\n 'canvas.fontLarger': '放大字号',\n 'canvas.bold': '加粗',\n 'canvas.textColor': '文字颜色',\n 'canvas.colorDefault': '跟随主题',\n 'canvas.close': '关闭',\n 'canvas.tabUpload': '上传',\n 'canvas.tabHistory': '历史记录',\n 'canvas.tabGallery': '素材库',\n 'canvas.imageMenuUpload': '本地图片',\n 'canvas.imageMenuAssets': '素材库',\n 'canvas.imageMenuHistory': '历史记录',\n 'canvas.imageMenuGenerate': 'AI生成',\n 'canvas.tabGenerate': 'AI 生成',\n 'canvas.dropHint': '图片拖动到此处即可添加',\n 'canvas.dropSub': '支持多选,PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': '未命名作品',\n 'canvas.picked': '已选 {count} 张',\n 'canvas.generateAndAdd': '生成并添加到画布',\n 'chat.expand': '展开右侧对话',\n 'chat.collapse': '收起右侧对话',\n 'chat.toggle': '对话',\n 'config.resizeHint': '拖拽调整参数栏宽度',\n 'ecommerce.generation': '生成设置',\n 'ecommerce.title': '电商模式',\n 'ecommerce.badge': '预览',\n 'ecommerce.short': '电商',\n 'ecommerce.anchorPending': '已生成主图,其余图片正在自动参考主图生成,保证整套同款…',\n 'ecommerce.anchorFailed': '主图生成失败,其余图片已暂停。请重试主图(或整组重新生成)后再继续。',\n 'ecommerce.anchorNote': '锚定生成:先出主图,其余图片自动参考主图,保证整套同款',\n 'ecommerce.noAssetWarn': '未上传商品图:主图将按文字描述生成并作为锚定基准,建议先上传商品主图获得更高一致性',\n 'ecommerce.product': '商品信息',\n 'ecommerce.productName': '商品名称(必填)',\n 'ecommerce.projectName': '项目名称(可选)',\n 'ecommerce.constraints': '卖点与约束',\n 'ecommerce.sellingPoints': '参考信息:品牌型号、规格(容量/尺寸/克重)、材质工艺、核心卖点、安全认证、配件清单等',\n 'ecommerce.protectedFeatures': '必须保留的特征,例如:颜色、Logo、包装文字、结构',\n 'ecommerce.styleHint': '风格补充,例如:高级感 / 写实 / 自然光',\n 'ecommerce.setStructure': '套图结构',\n 'ecommerce.preview': '生成套图预览',\n 'ecommerce.planTitle': '预计生成 {count} 张图片',\n 'ecommerce.planSlot': '{count} 张 · {description}',\n 'ecommerce.confirm': '确认生成整套图片',\n 'ecommerce.uploadRef': '上传商品图(必选,最多 4 张:主体 / 包装 / 细节)',\n 'ecommerce.uploadShort': '上传素材',\n 'ecommerce.sellingTitle': '商品卖点',\n 'ecommerce.advanced': '高级选项(平台 / 约束)',\n 'ecommerce.params': '参数选择',\n 'ecommerce.platformLabel': '平台',\n 'ecommerce.languageLabel': '文案语言',\n 'ecommerce.customLanguageOption': '自定义…',\n 'ecommerce.customLanguageLabel': '自定义语言',\n 'ecommerce.customLanguagePlaceholder': '输入语言名称,例如:Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': '比例',\n 'ecommerce.categoryLabel': '类目',\n 'ecommerce.multiSelect': '可多选',\n 'ecommerce.countHint': '点击调整数量(1-4)',\n 'ecommerce.refSettings': '参考图设置(可选)',\n 'ecommerce.styleTitle': '风格 / 补充提示',\n 'ecommerce.protectedLabel': '必须保留的特征(可选)',\n 'ecommerce.assetsFull': '最多上传 4 张商品图',\n 'ecommerce.productRefTitle': '主图参考图',\n 'ecommerce.productRefHint': '必选 · 商品主体图,最多 4 张',\n 'ecommerce.styleRefTitle': '风格参考图',\n 'ecommerce.styleRefBadge': '可选,最多 1 张',\n 'ecommerce.styleRefHint': '只传风格、光影、构图、氛围,不能更换商品主体。',\n 'ecommerce.styleRefUpload': '上传风格参考图',\n 'ecommerce.refInfoTitle': '参数信息',\n 'ecommerce.aiWrite': 'AI 帮写',\n 'ecommerce.aiWriting': 'AI 整理中…',\n 'ecommerce.aiWriteHint': '根据商品名称与类目自动整理参数信息(提示词)',\n 'ecommerce.promptInfoPlaceholder': '选填:品牌型号、规格(容量/尺寸/克重)、材质工艺、核心卖点、安全认证、配件清单、画面要求等,也可以点右上角 AI 帮写',\n 'ecommerce.optional': '可选',\n 'ecommerce.previewBoardTitle': '生成前预览',\n 'ecommerce.previewBoardHint': '每张图的提示词都可以在生成前修改',\n 'ecommerce.promptReset': '恢复默认',\n 'ecommerce.promptAutoHint': '自动生成 · 可直接修改',\n 'ecommerce.refSelect': '该用途使用的参考图',\n 'ecommerce.refNone': '不使用参考图',\n 'ecommerce.role.product': '商品主体',\n 'ecommerce.role.packaging': '包装',\n 'ecommerce.role.detail': '细节/角度',\n 'ecommerce.role.style': '风格参考',\n 'ecommerce.footerReady': '将生成 {count} 张套图',\n 'ecommerce.footerEmpty': '请选择套图结构',\n 'ecommerce.results.title': '套图结果',\n 'ecommerce.results.progress': '{done}/{total} 已完成',\n 'ecommerce.results.failed': '{count} 张失败',\n 'ecommerce.results.empty': '确认生成后,每张图片的任务状态和结果会按用途显示在这里',\n 'ecommerce.results.regenerate': '重新生成',\n 'ecommerce.results.export': '导出清单',\n 'ecommerce.results.newProduct': '新商品',\n // prompt\n 'prompt.placeholder': '描述你想要的画面,例如:一只戴着宇航员头盔的橘猫,在月球上举起望远镜,水彩风格,柔和光线…',\n 'prompt.required': '请输入提示词',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': '超出该模型 {limit} 字符的提示词上限,请精简后再生成',\n 'prompt.enhance': '增强',\n 'prompt.enhancing': '增强中…',\n 'prompt.enhanceHint': '使用已配置的对话模型扩写提示词',\n 'prompt.configTitle': '请先配置提示词增强模型',\n 'prompt.configHint': '已打开设置。进入「插件 → AI 生图」,填写或复用 API 地址和密钥,选择对话模型并保存。',\n 'tasks.title': '生成任务',\n 'tasks.queued': '排队中',\n 'tasks.running': '生成中',\n 'tasks.completed': '已完成',\n 'tasks.failed': '失败',\n 'tasks.cancelled': '已取消',\n 'tasks.cancel': '取消',\n 'tasks.retry': '重试',\n // parameters\n 'params.size': '尺寸',\n 'params.quality': '清晰度',\n 'params.count': '生成数量',\n 'params.detail': '细节',\n 'size.auto': '自动',\n 'size.square': '1:1 方图',\n 'size.portrait34': '3:4 标准竖图',\n 'size.landscape43': '4:3 标准横图',\n 'size.portrait916': '9:16 竖屏',\n 'size.portrait23': '2:3 竖图',\n 'size.landscape32': '3:2 横图',\n 'size.wide169': '16:9 宽屏',\n 'size.ultrawide21': '21:9 超宽屏',\n 'quality.auto': '自动',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1 张',\n 'count.two': '2 张',\n 'count.three': '3 张',\n 'count.four': '4 张',\n 'detail.auto': '自动',\n 'detail.standard': '标准',\n 'detail.high': '高清',\n 'detail.hint': '透传参数,部分 gpt-image-2 网关支持;官方接口请保持「自动」',\n // footer\n 'model.label': '模型',\n 'templates.favoritesCount': '共 {count} 个收藏',\n 'model.channel': '渠道',\n 'model.channelPlaceholder': '默认渠道',\n 'model.noEditModels': '当前模式暂无可用模型',\n 'compare.enable': '多模型对比',\n 'compare.models': '参与对比的模型',\n 'compare.title': '多模型结果对比',\n 'compare.fullscreen': '全屏对比',\n 'compare.selectRequired': '请至少选择一个对比模型',\n 'generate': '开始生成',\n 'generating': '生成中…',\n // edit reference image\n 'edit.upload': '点击或拖拽上传参考图片',\n 'edit.uploadHint': 'PNG / JPG / WEBP,不超过 10MB',\n 'edit.change': '更换图片',\n 'edit.remove': '移除',\n 'edit.required': '请先上传参考图片',\n // canvas\n 'canvas.emptyTitle': '开始你的创作',\n 'canvas.emptyHint': '双击空白处或用底部工具添加图片/文本节点;把图片和文本节点连线到目标节点,选中后即可在底部生成器中出图',\n 'canvas.new': '新建作图',\n 'canvas.newHint': '开始新的作图并清空预览',\n 'canvas.error': '生成失败:{error}',\n 'canvas.submitting': '正在加入生成队列…',\n 'canvas.queued': '已加入生成队列',\n 'canvas.queueHint': '当前有 {count} 个任务等待处理',\n 'canvas.generating': '正在生成图片…',\n 'canvas.elapsed': '已用时 {seconds}s',\n 'canvas.images': '本次生成 {count} 张',\n 'download': '下载',\n 'revisedPrompt': '优化提示词:{prompt}',\n // history\n 'history.title': '历史记录',\n 'history.empty': '暂无历史记录,生成图片后会保存在这里',\n 'history.clear': '清空',\n 'history.clearConfirm': '确定要清空全部历史记录吗?此操作不可撤销。',\n 'history.restore': '恢复',\n 'history.delete': '删除',\n 'history.images': '张',\n 'history.viewing': '历史 · {time}',\n 'history.search': '搜索提示词或模型…',\n 'history.model': '模型筛选',\n 'history.ratio': '比例筛选',\n 'history.allModels': '全部模型',\n 'history.allRatios': '全部比例',\n // gallery\n 'gallery.title': '素材库',\n 'gallery.categories': '分类',\n 'gallery.all': '全部素材',\n 'gallery.upload': '上传素材',\n 'gallery.uploadModel': '本地上传',\n 'gallery.uploaded': '素材已入库',\n 'gallery.uploadHint': '把本地图保存进素材库,画布随时可用',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': '画面比例',\n 'gallery.tags': '标签',\n 'gallery.filterHint': '按生成模式、模型和比例筛选素材',\n 'gallery.count': '· 共 {count} 幅',\n 'gallery.viewMode': '视图模式',\n 'gallery.masonry': '瀑布流',\n 'gallery.grid': '整齐网格',\n 'gallery.sort': '排序',\n 'gallery.newest': '最新发布',\n 'gallery.oldest': '最早发布',\n 'gallery.untitled': '未命名作品',\n 'gallery.search': '搜索作品或模型…',\n 'gallery.tagsPlaceholder': '标签,用逗号分隔',\n 'gallery.tagsApply': '添加标签',\n 'gallery.editTags': '编辑标签',\n 'gallery.tagsEditShort': '编辑',\n 'gallery.tagsSave': '保存',\n 'gallery.tagsCancel': '取消',\n 'gallery.selected': '已选 {count} 项',\n 'gallery.selectionDone': '完成选择',\n 'gallery.selectionClear': '取消选择',\n 'gallery.downloadSelected': '下载所选',\n 'gallery.exportJson': '导出 JSON',\n 'gallery.select': '选择作品',\n 'gallery.add': '加入素材库',\n 'gallery.added': '已加入素材库',\n 'gallery.already': '已在素材库中',\n 'gallery.delete': '移出素材库',\n 'gallery.openFolder': '打开文件夹',\n 'gallery.clear': '清空素材库',\n 'gallery.openFolderHint': '在系统的文件管理器中打开图片保存目录(~/.dsh/dsh-imagegen)',\n 'gallery.clearConfirm': '确定要清空整个素材库吗?此操作不可撤销。',\n 'gallery.empty': '素材库还是空的,把喜欢的图片加入进来吧',\n 'gallery.viewing': '素材库 · {time}',\n // preview\n 'preview.title': '图片预览',\n 'preview.open': '点击预览',\n 'preview.close': '关闭',\n 'preview.prev': '上一张',\n 'preview.next': '下一张',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': '图片缩放控制',\n 'preview.zoomIn': '放大',\n 'preview.zoomOut': '缩小',\n 'preview.zoomReset': '重置缩放',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': '复制提示词',\n 'preview.copied': '已复制',\n 'preview.addToEdit': '添加到图生图',\n // config banner\n 'config.missing': '尚未配置 API:请前往「设置 → 插件 → 可配置」为 AI 生图填写 api_url 与 api_key。',\n 'config.generationTitle': '请先配置生图 API',\n 'config.generationHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写生图 API 地址与 API 密钥,保存后即可开始生成。',\n 'config.enhancementTitle': '请先配置提示词增强模型',\n 'config.enhancementHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写或复用对话 API 地址和密钥,选择对话模型并保存。',\n 'config.disabledTitle': 'AI 生图插件已停用',\n 'config.disabledHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。开启「启用插件」后即可生成图片。',\n 'config.configured': '已连接 {url}',\n 'config.disabled': '插件已停用,请在设置中重新启用。',\n 'connection.connected': '已连接',\n 'connection.disconnected': '未连接',\n 'panel.collapseConfig': '收起参数栏',\n 'panel.expandConfig': '展开参数栏',\n // plugin update\n 'update.available': '检测到新版本:{version}',\n 'update.install': '在线更新',\n 'update.installing': '更新中…',\n 'update.success': '已更新到 {version},请重启 DSH',\n 'update.failed': '更新失败,请重试',\n 'update.release': '查看 Release',\n // settings card\n 'settings.title': 'AI 生图(dsh-imagegen)',\n 'settings.description': '配置图像生成 API 地址与密钥',\n 'settings.summary': '配置图像生成渠道、模型与插件开关',\n 'settings.currentVersion': '当前版本',\n 'settings.apiUrl': 'API 地址(api_url)',\n 'settings.apiUrlHint': 'OpenAI 兼容接口基址,如 https://api.openai.com/v1;将自动拼接 /images/generations 与 /images/edits',\n 'settings.apiKey': 'API 密钥(api_key)',\n 'settings.apiKeyHint': 'Bearer 密钥,明文存于本机设置文档;界面只显示是否已设置',\n 'settings.apiKeySet': '已保存密钥;输入新值可更换,点击「清除」可删除',\n 'settings.apiKeyClear': '清除',\n 'settings.imageModelsTitle': '生图模型',\n 'settings.imageModelsHint': '保存 API 地址和密钥后检测候选模型;请只选择实际支持生图的项。',\n 'settings.imageModels': '允许使用的生图模型',\n 'settings.imageModelsManualHint': '一行一个模型;API 未提供 /models 时可手动填写。面板和 Agent 只能使用此列表。',\n 'settings.imageModelsFetch': '检测可用模型',\n 'settings.imageModelsLoading': '检测中…',\n 'settings.imageModelsCandidates': '检测到的候选模型(勾选后保存)',\n 'settings.addModel': '+ 手动添加',\n 'settings.cancelAddModel': '收起添加',\n 'settings.addModelPlaceholder': '输入模型名称,例如 qwen-image',\n 'settings.addModelConfirm': '添加',\n 'settings.removeModel': '移除模型',\n 'settings.optional': '可选',\n 'settings.moreOptions': '更多设置',\n 'settings.promptEnhanceTitle': '提示词增强模型',\n 'settings.promptEnhanceHint': '用于将简短描述扩写为更完整的生图提示词;留空的地址和密钥会复用生图配置。',\n 'settings.promptApiUrl': '对话 API 地址(可选)',\n 'settings.promptApiUrlHint': 'OpenAI 兼容基址;留空则复用图像 API 地址。',\n 'settings.promptApiKey': '对话 API 密钥(可选)',\n 'settings.promptApiKeyHint': '留空则复用图像 API 密钥。',\n 'settings.promptModel': '对话模型',\n 'settings.promptModelHint': '选择或填写支持 /chat/completions 的模型。',\n 'settings.promptModelDetectionHint': '默认复用生图 API;检测后点选一个模型即可。',\n 'settings.promptModelsFetch': '获取可用模型',\n 'settings.promptModelsLoading': '正在获取…',\n 'settings.promptModelsSelect': '选择一个模型',\n 'settings.promptModelsCandidates': '检测到的候选对话模型',\n 'settings.addPromptModelPlaceholder': '输入对话模型名称,例如 gpt-4.1-mini',\n 'settings.promptApiAdvanced': '使用独立对话 API(可选)',\n 'settings.announceToAgent': '向 Agent 播报本插件',\n 'settings.announceToAgentHint': '开启后,本插件的存在与能力会写入每个 Agent 的系统提示词',\n 'settings.allowAgentImageGeneration': '允许 Agent 调用生图',\n 'settings.allowAgentImageGenerationHint': '默认开启。关闭后,Agent 无法提交、查询或取消生图任务;侧边栏工作台不受影响。',\n 'settings.enabled': '启用插件',\n 'settings.enabledHint': '关闭后生图面板不可用(设置卡片始终可用)',\n 'settings.save': '保存',\n 'settings.saving': '保存中…',\n 'settings.discard': '放弃修改',\n 'settings.unsaved': '有未保存的修改',\n 'settings.saveFailed': '保存失败,请重试',\n 'settings.readOnly': '当前设置文档为只读,无法保存。',\n 'settings.notExposed': '设置命名空间不可用:本部署未提供该插件的设置服务。',\n 'settings.expand': '展开',\n 'settings.collapse': '收起',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '重置',\n 'settings.invalidNumber': '请输入有效数字',\n\n\n\n // template library\n 'templates.open': '模板库',\n 'templates.title': '提示词模板库',\n 'templates.sources': '模板库来源',\n 'templates.meta': '共 {count} 个模板 · {origin}',\n 'templates.origin.bundled': '内置快照',\n 'templates.origin.refreshed': '在线刷新',\n 'templates.search': '搜索模板标题或提示词…',\n 'templates.all': '全部',\n 'templates.close': '关闭',\n 'templates.back': '返回列表',\n 'templates.use': '使用此提示词',\n 'templates.copy': '复制提示词',\n 'templates.copied': '已复制',\n 'templates.refresh': '刷新本库',\n 'templates.refreshing': '刷新中…',\n 'templates.refreshed': '已刷新,共 {count} 个模板',\n 'templates.refreshFailed': '刷新失败:{error}',\n 'templates.favorites': '收藏',\n 'templates.favoritesHint': '只看已收藏的模板',\n 'templates.favoritesEmpty': '还没有收藏,点击模板卡片右上角的星标即可收藏',\n 'templates.favoriteAdd': '收藏此模板',\n 'templates.favoriteRemove': '取消收藏',\n 'templates.favorite': '收藏',\n 'templates.unfavorite': '已收藏',\n 'templates.cacheAll': '缓存全部图片',\n 'templates.cacheAllHint': '通过本机代理把当前模板库的全部参考图缓存到本地磁盘,之后离线也能浏览',\n 'templates.caching': '缓存中 {done}/{total}…',\n 'templates.cached': '图片已全部缓存',\n 'templates.empty': '没有匹配的模板',\n 'templates.loading': '正在加载模板库…',\n 'templates.loadFailed': '模板库加载失败:{error}',\n 'templates.retry': '重试',\n 'templates.attribution': '模板与图片来自各来源站点,作者链接见模板详情',\n 'templates.source': '来源:{label}',\n 'templates.featured': '精选',\n // inspiration wall (studio empty state)\n 'inspiration.title': '灵感案例',\n 'inspiration.shuffle': '随机',\n 'inspiration.shuffling': '换一批…',\n 'inspiration.useHint': '点击使用该提示词',\n // channel management\n 'channels.title': '渠道',\n 'channels.hint': '填写各渠道的 API 地址与密钥即可使用对应模型',\n 'channels.empty': '尚无渠道,点击下方按钮添加',\n 'channels.addProvider': '添加提供方',\n 'channels.addCustom': '添加自定义渠道',\n 'channels.untitled': '未命名渠道',\n 'channels.keySet': '密钥已设置',\n 'channels.keyMissing': '未设置密钥',\n 'channels.modelCount': '{n} 个模型',\n 'channels.noModels': '未配置模型',\n 'channels.defaultLabel': '默认',\n 'channels.statusReady': '可用',\n 'channels.statusIncomplete': '配置未完成',\n 'channels.edit': '编辑',\n 'channels.delete': '删除',\n 'channels.deleteConfirmTitle': '删除渠道「{name}」?该渠道的密钥将被移除;历史与素材库中的记录仍会保留。',\n 'channels.confirm': '确认删除',\n 'channels.cancel': '取消',\n 'channels.editorTitle': '渠道',\n 'channels.editorSaveNote': '改动将随卡片底部「保存」一起生效',\n 'channels.displayName': '显示名称',\n 'channels.apiUrl': 'API 地址',\n 'channels.apiKey': 'API 密钥',\n 'channels.keyReplaceHint': '已配置 — 输入新值可替换',\n 'channels.keyMissingHint': '填写密钥',\n 'channels.keyClear': '清除密钥',\n 'channels.modelCatalogTitle': '模型目录',\n 'channels.noModelsHint': '暂无模型:点击「检测」拉取候选,或手动添加;别名(显示名)默认等于上游模型 id,可自行修改。',\n 'channels.detect': '重新检测',\n 'channels.detecting': '检测中…',\n 'channels.detectSuccess': '连通 ✓ · {n} 个候选',\n 'channels.detectFailed': '检测失败:{error}',\n 'channels.detectOk': '已连接该 API',\n 'channels.modelAliasLabel': '显示名(别名)',\n 'channels.modelIdLabel': '上游模型 id',\n 'channels.unknownProtocol': '未知协议,按通用 OpenAI 协议尝试',\n 'channels.removeModel': '移除模型',\n 'channels.manualAddPlaceholder': '输入模型 id,例如 qwen-image',\n 'channels.addModelConfirm': '添加',\n 'channels.copyFrom': '从其他渠道复制…',\n 'channels.copyApply': '复制',\n 'channels.candidatesTitle': '图片模型候选(点击加入)',\n 'channels.deleteThisChannel': '删除此渠道',\n 'channels.setDefault': '设为默认渠道',\n 'channels.presetPickerTitle': '添加提供方',\n 'channels.presetPickerHint': '选择一个内置提供方,仅需填写 API 密钥',\n 'channels.presetCustomHint': '自行填写 API 地址、密钥与模型目录',\n 'channels.presetLoadFailed': '加载提供方失败:{error}',\n 'canvas.skills.fileNode': '文件节点',\n 'canvas.skills.addFileNode': '添加文件节点',\n 'canvas.skills.emptyFileNode': '点击上传文件',\n 'canvas.skills.fileMenuUpload': '上传文件',\n 'canvas.skills.fileDropHint': '文件拖到此处即可上传',\n 'canvas.skills.fileDropSub': '支持 PDF / Office / 文本 / 图片等,≤50MB',\n 'canvas.skills.fileSizeUnknown': '大小未知',\n 'canvas.skills.filePreviewUnavailable': '此格式无法内联预览,请下载或用技能处理',\n 'canvas.skills.filePreviewLoading': '正在读取预览…',\n 'canvas.skills.fileDownload': '下载文件',\n 'canvas.skills.fileOpen': '用系统程序打开',\n 'canvas.skills.fileKind.text': '文本',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': '图片',\n 'canvas.skills.fileKind.office': '文档',\n 'canvas.skills.fileKind.archive': '压缩包',\n 'canvas.skills.fileKind.audio': '音频',\n 'canvas.skills.fileKind.video': '视频',\n 'canvas.skills.fileKind.other': '文件',\n 'canvas.skills.fileTooLarge': '文件超过 {size}MB 上限',\n 'canvas.skills.fileTypeBlocked': '出于安全考虑,不支持上传 {ext} 文件',\n 'canvas.skills.fileUploadFailed': '文件上传失败:{message}',\n 'canvas.preview.expand': '放大预览',\n 'canvas.preview.close': '关闭预览',\n 'canvas.preview.copy': '复制内容',\n 'canvas.preview.copied': '已复制',\n 'canvas.preview.openTab': '新标签页打开',\n 'canvas.preview.retry': '重试',\n 'canvas.preview.failed': '读取预览失败:{message}',\n 'canvas.preview.empty': '文件是空的',\n 'canvas.preview.textTruncated': '文件较大,仅显示前 {chars} 个字符',\n 'canvas.preview.tableTruncated': '表格较大,仅显示前 {rows} 行',\n 'canvas.preview.archiveTruncated': '压缩包条目较多,仅列出前 {count} 项',\n 'canvas.preview.docTruncated': '文档较长,仅显示前 {blocks} 段',\n 'canvas.preview.slidesTruncated': '幻灯片较多,仅显示前 {count} 页',\n 'canvas.preview.slideOf': '第 {page} 页',\n 'canvas.preview.rendered': '渲染',\n 'canvas.preview.source': '源码',\n 'canvas.preview.pdfFallback': '当前环境无法内联显示 PDF,请下载后查看',\n 'canvas.preview.pdfHeuristic': 'PDF 文字为启发式提取,复杂排版可能丢失顺序',\n 'canvas.preview.reason.unsupported': '这种格式没有可读内容,请下载后查看,或先转成图片 / 文本节点再交给技能处理',\n 'canvas.preview.reason.unreadable': '没能从这个文件里读出文字,可能是扫描件或特殊编码;可以转成图片节点交给视觉模型',\n 'canvas.preview.doubleClick': '双击放大预览',\n 'canvas.skills.button': '技能',\n 'canvas.skills.menuTitle': '运行技能',\n 'canvas.skills.runOn': '在「{name}」上运行技能',\n 'canvas.skills.pickerTitle': '选择技能',\n 'canvas.skills.searchPlaceholder': '搜索技能…',\n 'canvas.skills.groupBuiltin': '内置动作',\n 'canvas.skills.groupExternal': '本机技能',\n 'canvas.skills.emptyExternal': '本机没有可用技能,可在 ~/.dsh/skills 安装后重试',\n 'canvas.skills.empty': '没有可用技能',\n 'canvas.skills.loading': '正在读取技能列表…',\n 'canvas.skills.tierLight': '轻量',\n 'canvas.skills.tierHeavy': '重任务',\n 'canvas.skills.accepts': '可用节点:{types}',\n 'canvas.skills.incompatible': '该技能不适用于此节点类型',\n 'canvas.skills.disabled': '画布技能已在设置中关闭',\n 'canvas.skills.noRegistry': '本宿主未挂载技能注册表(ctx.skills),只能使用内置动作',\n 'canvas.skills.registryFailed': '读取技能列表失败:{message}',\n 'canvas.skills.needAgent': '需要宿主 Agent 运行层(本机 DSH 未提供),重任务技能不可用',\n 'canvas.skills.unknown': '技能不存在或已被移除',\n 'canvas.skills.canvasMissing': '画布不存在,请刷新后重试',\n 'canvas.skills.noInput': '这个节点没有可处理的输入',\n 'canvas.skills.noChat': '轻量技能需要先在「设置 → 插件 → AI 生图 → 提示词增强」配置聊天模型',\n 'canvas.skills.noDescription': '(技能未提供描述)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': '你在执行名为「{name}」的技能。严格按技能说明处理用户提供的输入,只用用户语言回答,直接输出处理结果,不要复述技能说明。',\n 'canvas.skills.externalHeader': '请按技能「{name}」的说明处理下面的输入。',\n 'canvas.skills.externalHeavyCost': '该技能属于长任务,会启动无头 Agent 执行,可能长时间占用模型额度。',\n 'canvas.skills.extraInstruction': '额外要求:',\n 'canvas.skills.inputsHeader': '输入内容:',\n 'canvas.skills.inputsEmpty': '(没有可读取的文本内容,请依据附带的图片处理)',\n 'canvas.skills.needInstruction': '请填写自定义润色指令',\n 'canvas.skills.needImage': '该技能需要图片节点作为输入',\n 'canvas.skills.needText': '该技能需要带文字的文本节点作为输入',\n 'canvas.skills.fileMissing': '文件资产不存在或已被清理',\n 'canvas.skills.extractFailed': '无法抽取该文件的内容',\n 'canvas.skills.nothingToRead': '没有可读取的文本内容',\n 'canvas.skills.truncated': '内容较长,已截断到 {count} 字符',\n 'canvas.skills.tooManyImages': '图片数量超过 {count} 张,只处理了前面的图片',\n 'canvas.skills.extractNodeTitle': '「{name}」内容',\n 'canvas.skills.summaryHead': '已汇总 {count} 个来源节点:',\n 'canvas.skills.describeAsk': '请描述这 {count} 张图片。',\n 'canvas.skills.polishNodeTitle': '润色结果({style})',\n 'canvas.skills.skillMissing': '本机未安装「{name}」技能。请先安装该技能(例如让 DSH Agent 安装 https://github.com/ningzimu/image-to-editable-ppt-skill ),再重试。',\n 'canvas.skills.agentFailed': '无法创建无头 Agent(请检查模型与 Agent 预设配置)',\n 'canvas.skills.needModel': '宿主没有提供默认模型(agent-default-model),重任务技能无法启动 Agent。请先在「设置 → 通用」选择默认模型,再重试。',\n 'canvas.skills.noOutputFile': '技能没有产出文件,已把 Agent 的回复保存为文本节点',\n 'canvas.skills.heavyIntro': '你正在为一个可视化画布执行技能「{name}」。严格按技能说明完成整个流程,不要省略步骤。',\n 'canvas.skills.heavyBodyHeader': '技能说明:',\n 'canvas.skills.heavyInputsHeader': '本任务的输入文件(绝对路径):',\n 'canvas.skills.heavyOutputHeader': '把最终产物写入这个目录:{dir}',\n 'canvas.skills.heavyRules': '要求:\\n1. 全部中间文件放在当前工作目录,最终产物只能放在上面的输出目录。\\n2. 完成后用简短中文回复:产物文件名、是否成功、注意事项。',\n 'canvas.skills.heavyAsk': '请执行技能「{name}」。',\n 'canvas.skills.heavyAskInputs': '输入文件目录:{dir}',\n 'canvas.skills.heavyAskOutput': '输出目录:{dir}',\n 'canvas.skills.heavyAskLoadSkill': '(如果技能说明不在上下文里,请先用技能名「{name}」加载它)',\n 'canvas.skills.stageQueued': '排队中',\n 'canvas.skills.stagePreparing': '准备输入文件',\n 'canvas.skills.stageReading': '读取节点内容',\n 'canvas.skills.stageRunning': '技能执行中',\n 'canvas.skills.stageCollecting': '收集产物',\n 'canvas.skills.stageDone': '完成',\n 'canvas.skills.stageCancelled': '已取消',\n 'canvas.skills.stagePrepared': '已备好 {count} 个输入文件',\n 'canvas.skills.stageArtifacts': '技能执行中 · 已产出 {count} 个文件',\n 'canvas.skills.runElapsed': '已用时',\n 'canvas.skills.runRetry': '重试',\n 'canvas.skills.runStarted': '技能「{name}」已开始',\n 'canvas.skills.runDone': '技能完成,新增 {count} 个节点',\n 'canvas.skills.runFailed': '技能运行失败:{message}',\n 'canvas.skills.runCancelled': '技能运行已取消',\n 'canvas.skills.cancel': '取消运行',\n 'canvas.skills.cancelling': '正在取消…',\n 'canvas.skills.confirmTitle': '运行重任务技能',\n 'canvas.skills.confirmBody': '技能「{name}」会启动一个无头 Agent 在本机执行,可能持续数分钟到数十分钟,并消耗较多模型额度。',\n 'canvas.skills.confirmCost': '成本提示:{hint}',\n 'canvas.skills.confirmRequires': '前置条件:{list}',\n 'canvas.skills.confirmRun': '确认运行',\n 'canvas.skills.confirmCancel': '先不运行',\n 'canvas.skills.batchTitle': '批量运行',\n 'canvas.skills.batchNote': '将依次在 {count} 个节点上运行(串行执行)',\n 'canvas.skills.batchTooMany': '一次最多处理 {count} 个节点',\n 'canvas.skills.sourceBadge': '技能产出',\n 'canvas.skills.sourceTip': '由技能「{name}」产出,来自 {count} 个来源节点',\n 'canvas.skills.rerun': '用同一技能再跑一次',\n 'canvas.skills.history': '技能运行记录',\n 'canvas.skills.historyEmpty': '暂无运行记录',\n 'canvas.skills.needConfirm': '该技能需要确认后才能运行',\n 'canvas.skills.polish': '文本润色',\n 'canvas.skills.polishDesc': '按风格改写文本节点:更正式 / 更口语 / 更短 / 扩写,或自定义指令',\n 'canvas.skills.describe': '图片描述',\n 'canvas.skills.describeDesc': '用视觉模型读出图片内容,产出文本节点(可用作提示词)',\n 'canvas.skills.extract': '内容抽取',\n 'canvas.skills.extractDesc': '把文件或图片里的文字抽成文本节点(文本 / 代码 / OOXML 文档 / PDF)',\n 'canvas.skills.ppt': '图片转可编辑 PPT',\n 'canvas.skills.pptDesc': '调用本机 image-to-editable-ppt 技能,把图片重建为可编辑 .pptx 文件节点',\n 'canvas.skills.pptCost': '长任务:10 页 PPT 可能消耗较多额度,单页通常 10 分钟以上',\n 'canvas.skills.pptRequires': '需要本机已安装 image-to-editable-ppt 技能,并按其文档配置 OCR Token 与图片后端',\n 'canvas.polish.button': 'AI 润色',\n 'canvas.polish.title': 'AI 润色',\n 'canvas.polish.formal': '更正式',\n 'canvas.polish.casual': '更口语',\n 'canvas.polish.shorter': '更短',\n 'canvas.polish.expand': '扩写',\n 'canvas.polish.custom': '自定义',\n 'canvas.polish.customPlaceholder': '例如:改写成小红书文案,带 emoji 和话题标签',\n 'canvas.polish.run': '开始润色',\n 'canvas.polish.previewTitle': '原 / 新对比',\n 'canvas.polish.apply': '应用',\n 'canvas.polish.revert': '还原',\n 'canvas.polish.applied': '已应用润色结果',\n 'canvas.polish.directHint': '当前设置:润色后直接覆盖原文(可在设置中改为先预览)',\n 'settings.skillsEnabled': '画布技能',\n 'canvas.skills.libraryButton': '技能库',\n 'canvas.skills.libraryTitle': '技能库管理',\n 'canvas.skills.libraryInstalled': '已安装技能({count})',\n 'canvas.skills.libraryEmpty': '本机还没有安装任何技能。可以从仓库链接安装,或上传技能压缩包。',\n 'canvas.skills.libraryRoot': '安装目录:{root}',\n 'canvas.skills.libraryInstall': '在线安装',\n 'canvas.skills.libraryInstallHint': '支持 GitHub 仓库 / 目录 / 单个 SKILL.md、任意 git 地址、zip 压缩包地址;多个地址用换行分隔。安装后宿主动态加载,无需重启。',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo 或 …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': '上传压缩包',\n 'canvas.skills.libraryUploadHint': '上传一个包含 SKILL.md 的 zip(单文件 ≤50MB),解压后安装到 ~/.dsh/skills。',\n 'canvas.skills.libraryDropzone': '点击选择或拖入 .zip 压缩包',\n 'canvas.skills.libraryForce': '覆盖已安装的同名技能',\n 'canvas.skills.libraryInstallAction': '安装',\n 'canvas.skills.libraryInstalling': '安装中…',\n 'canvas.skills.libraryRemove': '卸载',\n 'canvas.skills.libraryRemoving': '卸载中…',\n 'canvas.skills.libraryRemoveConfirm': '确定卸载技能「{name}」?该技能目录会被删除。',\n 'canvas.skills.libraryInstalledToast': '已安装技能:{names}',\n 'canvas.skills.libraryFailedToast': '安装失败:{message}',\n 'canvas.skills.libraryRemovedToast': '已卸载技能:{name}',\n 'canvas.skills.libraryUnavailable': '本宿主不支持管理技能库',\n 'canvas.skills.libraryReload': '刷新',\n 'canvas.skills.libraryOpenFolder': '打开技能目录',\n 'canvas.skills.installNow': '去安装',\n 'canvas.skills.needsInstall': '未安装',\n 'canvas.skills.missingHint': '技能「{name}」还没安装,点击安装即可使用',\n 'canvas.skills.installArchiveFailed': '压缩包安装失败:{message}',\n 'canvas.skills.libraryIssueFrontmatter': 'SKILL.md 缺少 YAML frontmatter(name / description 必填),本机技能注册表不会加载它,节点里也用不了',\n 'canvas.skills.libraryIssueName': 'frontmatter 的 name「{name}」不是 kebab-case(只能小写字母、数字和连字符),本机技能注册表会忽略它,节点里也用不了',\n 'canvas.skills.libraryIssueDescription': 'SKILL.md 的 frontmatter 缺少 description(必填),本机技能注册表不会加载它,节点里也用不了',\n 'canvas.skills.libraryIssueInvocation': 'disable-model-invocation / user-invocable 的写法无法解析,本机技能注册表会忽略它,节点里也用不了',\n 'canvas.skills.libraryIssueUserOnly': '该技能声明了 disable-model-invocation,只允许人工调用,画布节点无法运行它',\n // Skill configuration (declared in skill.config.json, see docs/skill-config.md)\n 'canvas.skills.configOpen': '配置',\n 'canvas.skills.configSave': '保存',\n 'canvas.skills.configSaveApply': '保存并应用',\n 'canvas.skills.configSaving': '保存中…',\n 'canvas.skills.configApplying': '应用中…',\n 'canvas.skills.configSaved': '已保存',\n 'canvas.skills.configSaveFailed': '保存失败',\n 'canvas.skills.configApplied': '已保存并应用',\n 'canvas.skills.configApplyFailed': '应用失败',\n 'canvas.skills.configClear': '清除',\n 'canvas.skills.configSecretSet': '已设置(留空表示不改动)',\n 'canvas.skills.configSecretUnset': '未设置',\n 'canvas.skills.configMissingHint': '需要配置:{fields}',\n 'canvas.skills.configGo': '去配置',\n 'canvas.skills.configSourceSkill': '配置项由该技能自己的 skill.config.json 声明',\n 'canvas.skills.configSourcePlugin': '配置项来自本插件内置的已知技能配方(技能自带声明会优先)',\n 'canvas.skills.configHeader': '已配置的值:',\n 'canvas.skills.configUnwritable': '本宿主无法写入设置,技能配置暂不可用',\n 'canvas.skills.configIssueVersion': 'skill.config.json 的 version 不是 1,已忽略这份声明',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json 无法解析(JSON 语法错误),已忽略这份声明',\n 'canvas.skills.configIssueEmpty': 'skill.config.json 里没有可用的字段或步骤,已忽略这份声明',\n} as const\n\nexport const en: Record<keyof typeof zh, string> = {\n 'entry.label': 'AI Image',\n 'entry.tooltip': 'AI image generation studio (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream family)',\n 'entry.newSession': 'New session',\n 'entry.newSessionTooltip': 'Create a new DSH session',\n 'entry.image': 'Image',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': 'Like it or have suggestions? Head to GitHub to open issues and star us!',\n 'conversation.add': 'Add to chat',\n 'conversation.adding': 'Adding…',\n 'conversation.added': 'Image added to the current chat',\n 'conversation.addHint': 'After adding to chat, use /edit_image to modify it; the command uses the plugin image model',\n 'conversation.noSession': 'Open a session before adding an image',\n 'conversation.unavailable': 'The current session is unavailable',\n 'conversation.busy': 'The chat is sending a message; try again shortly',\n 'mode.text': 'Text to Image',\n 'mode.edit': 'Image to Image',\n 'workspace.label': 'Workspace',\n 'workspace.normal': 'Standard',\n 'workspace.canvas': 'Infinite canvas',\n 'workspace.ecommerce': 'E-commerce',\n 'canvas.addImage': 'Add image',\n 'canvas.addText': 'Add text',\n 'canvas.generate': 'Generate image',\n 'canvas.addToCanvas': 'Add to canvas',\n 'canvas.untitled': 'Untitled canvas',\n 'canvas.imageNode': 'Image node',\n 'canvas.textNode': 'Text node',\n 'canvas.fromGallery': 'Gallery image',\n 'canvas.fromHistory': 'History image',\n 'canvas.project': 'Canvas project',\n 'canvas.newCanvas': 'New canvas',\n 'canvas.deleteCanvas': 'Delete canvas',\n 'canvas.deleteCanvasConfirm': 'Click again to confirm',\n 'canvas.rename': 'Rename canvas',\n 'canvas.renameHint': 'Double-click to rename',\n 'canvas.background': 'Canvas background',\n 'canvas.backgroundDots': 'Dots',\n 'canvas.backgroundLines': 'Grid lines',\n 'canvas.backgroundWaves': 'Waves',\n 'canvas.backgroundShapeGrid': 'Scrolling grid',\n 'canvas.backgroundDotField': 'Dot field',\n 'canvas.backgroundDotGrid': 'Interactive dots',\n 'canvas.backgroundFloatingLines': 'Floating lines',\n 'canvas.backgroundFlow': 'Fluid flow',\n 'canvas.backgroundLiquid': 'Liquid ether',\n 'canvas.backgroundFaultyTerminal': 'Faulty terminal',\n 'canvas.backgroundSilk': 'Silk',\n 'canvas.backgroundGalaxy': 'Galaxy',\n 'canvas.backgroundBlank': 'Blank',\n 'canvas.backgroundUpload': 'Upload custom background…',\n 'canvas.backgroundRemove': 'Remove custom background',\n 'canvas.templateLibrary': 'Template library (pick a prompt to create text + config nodes)',\n 'canvas.undo': 'Undo (Ctrl+Z)',\n 'canvas.redo': 'Redo (Ctrl+Shift+Z)',\n 'canvas.saving': 'Saving…',\n 'canvas.saved': 'Saved',\n 'canvas.saveFailed': 'Save failed',\n 'canvas.loading': 'Loading…',\n 'canvas.toolSelect': 'Select / move nodes',\n 'canvas.toolPan': 'Pan canvas (Space / Ctrl + drag)',\n 'canvas.delete': 'Delete',\n 'canvas.duplicate': 'Duplicate (Ctrl+D)',\n 'canvas.download': 'Download image',\n 'canvas.fitView': 'Fit all content',\n 'canvas.minimapOpen': 'Open minimap',\n 'canvas.minimapClose': 'Close minimap',\n 'canvas.minimap': 'Canvas minimap',\n 'canvas.zoom': 'Zoom',\n 'canvas.textPlaceholder': 'Type text…',\n 'canvas.generatingNode': 'Generating…',\n 'canvas.generateFailed': 'Generation failed',\n 'canvas.retry': 'Retry',\n 'canvas.taskLost': 'Generation task lost (service restarted); please retry',\n 'canvas.emptyImageNode': 'Click to upload an image',\n 'canvas.connectHint': 'Drag onto another node to connect',\n 'canvas.connectAddHint': 'Drag onto another node to connect, or click to add the next node',\n 'canvas.resizeHint': 'Drag to resize (image ratio locked)',\n 'canvas.count': 'Image count',\n 'canvas.configNode': 'Generation config',\n 'canvas.addConfigNode': 'Add config node',\n 'canvas.configHint': 'Connect image nodes on the left as references and text nodes as prompts; select this node to write the prompt in the composer below and generate.',\n 'canvas.composerLinked': '{count} input node(s) connected',\n 'canvas.composerPlaceholder': 'Describe the image to generate; or connect text nodes as the prompt…',\n 'canvas.countUnit': '{count}',\n 'canvas.model': 'Image model',\n 'canvas.modelPlaceholder': 'Choose a model',\n 'canvas.size': 'Size ratio',\n 'canvas.sizeAuto': 'Auto ratio',\n 'canvas.quality': 'Quality',\n 'canvas.qualityAuto': 'Auto quality',\n 'canvas.needApi': 'Configure the image API first',\n 'canvas.needPrompt': 'Enter a generation prompt first',\n 'canvas.needModel': 'No image model available',\n 'canvas.openSettings': 'Open settings',\n 'canvas.deleteConnection': 'Delete connection',\n 'canvas.paste': 'Paste (Ctrl+V)',\n 'canvas.addImageNode': 'Add image node',\n 'canvas.addTextNode': 'Add text node',\n 'canvas.addSketch': 'Add sketch board (draw-to-generate)',\n 'canvas.sketchNode': 'Sketch board',\n 'canvas.addSketchNode': 'Add sketch board node',\n 'canvas.sketchPlaceholder': 'Draw a sketch freely, then connect it to a config node as the reference image',\n 'canvas.sketchBrush': 'Brush',\n 'canvas.sketchEraser': 'Eraser',\n 'canvas.sketchUndo': 'Undo last stroke',\n 'canvas.sketchClear': 'Clear board',\n 'canvas.sketchWidth': 'Stroke width',\n 'canvas.dismiss': 'Dismiss',\n 'canvas.annotate': 'Annotate: draw a box to attach a prompt card',\n 'canvas.annotationNode': 'Annotation prompt',\n 'canvas.annotationTag': 'Annotated',\n 'canvas.annotationPlaceholder': 'Describe what this box should become, e.g. replace it with an orange cat',\n 'canvas.annotationDrawHint': 'Drag on the image to draw a box; releasing attaches a prompt card that follows the image (no connection needed)',\n 'canvas.removeBackground': 'Remove background (local matting, transparent PNG)',\n 'canvas.removingBackground': 'Removing background…',\n 'canvas.removeBgDone': 'Background removed ({percent}% transparent)',\n 'canvas.removeBgFailed': 'The background is too complex for local matting; box the subject with 标注 and let the model redraw it',\n 'canvas.alreadyTransparent': 'This image already has a transparent background',\n 'canvas.noBackgroundNode': 'Cut-out image',\n 'canvas.splitLayers': 'Split layers (AI decomposition into editable layers)',\n 'canvas.splittingLayers': 'Splitting layers…',\n 'canvas.layerSplitDone': 'Split into {count} layer node(s)',\n 'canvas.layerSplitEmpty': 'The model found no layers to split',\n 'canvas.layerBackground': 'Background · {label}',\n 'canvas.layerObject': 'Layer · {label}',\n 'canvas.layerText': 'Text · {label}',\n 'canvas.layerKindBackground': 'Background',\n 'canvas.layerKindObject': 'Object',\n 'canvas.layerKindText': 'Text',\n 'canvas.nodeModel': 'Model used by this node (retry and connected config nodes)',\n 'canvas.modelDefault': 'Default model',\n 'canvas.fontSmaller': 'Smaller font',\n 'canvas.fontLarger': 'Larger font',\n 'canvas.bold': 'Bold',\n 'canvas.textColor': 'Text color',\n 'canvas.colorDefault': 'Theme default',\n 'canvas.close': 'Close',\n 'canvas.tabUpload': 'Upload',\n 'canvas.tabHistory': 'History',\n 'canvas.tabGallery': 'Assets',\n 'canvas.imageMenuUpload': 'Local image',\n 'canvas.imageMenuAssets': 'Assets',\n 'canvas.imageMenuHistory': 'History',\n 'canvas.imageMenuGenerate': 'AI generate',\n 'canvas.tabGenerate': 'AI generate',\n 'canvas.dropHint': 'Drop images here to add them',\n 'canvas.dropSub': 'Multiple files, PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': 'Untitled work',\n 'canvas.picked': '{count} selected',\n 'canvas.generateAndAdd': 'Generate and add to canvas',\n 'chat.expand': 'Expand the conversation pane',\n 'chat.collapse': 'Collapse the conversation pane',\n 'chat.toggle': 'Chat',\n 'config.resizeHint': 'Drag to resize the parameter panel',\n 'ecommerce.generation': 'Generation settings',\n 'ecommerce.title': 'E-commerce Mode',\n 'ecommerce.badge': 'Beta',\n 'ecommerce.short': 'Store',\n 'ecommerce.anchorPending': 'Main image generated — remaining images are being generated with the main image as reference to keep the set consistent…',\n 'ecommerce.anchorFailed': 'Main image failed; remaining images are paused. Retry the main image or regenerate the set.',\n 'ecommerce.anchorNote': 'Anchored: main image first, remaining images reference it automatically for a consistent set',\n 'ecommerce.noAssetWarn': 'No product image uploaded: the main image will be generated from text as the anchor. Upload one for higher consistency.',\n 'ecommerce.product': 'Product details',\n 'ecommerce.productName': 'Product name (required)',\n 'ecommerce.projectName': 'Project name (optional)',\n 'ecommerce.constraints': 'Selling points & constraints',\n 'ecommerce.sellingPoints': 'Reference info: brand & model, specs (capacity/size/weight), materials, key selling points, certifications, accessories…',\n 'ecommerce.protectedFeatures': 'Features to preserve: color, logo, packaging text, structure',\n 'ecommerce.styleHint': 'Style hint, e.g. premium / realistic / natural light',\n 'ecommerce.setStructure': 'Image set structure',\n 'ecommerce.preview': 'Preview product set',\n 'ecommerce.planTitle': 'Plan to generate {count} images',\n 'ecommerce.planSlot': '{count} images · {description}',\n 'ecommerce.confirm': 'Confirm and generate set',\n 'ecommerce.uploadRef': 'Upload product images (required, up to 4: product / packaging / detail)',\n 'ecommerce.uploadShort': 'Upload',\n 'ecommerce.sellingTitle': 'Selling points',\n 'ecommerce.advanced': 'Advanced (platform / constraints)',\n 'ecommerce.params': 'Parameters',\n 'ecommerce.platformLabel': 'Platform',\n 'ecommerce.languageLabel': 'Copy language',\n 'ecommerce.customLanguageOption': 'Custom…',\n 'ecommerce.customLanguageLabel': 'Custom language',\n 'ecommerce.customLanguagePlaceholder': 'Enter a language, e.g. Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': 'Aspect ratio',\n 'ecommerce.categoryLabel': 'Category',\n 'ecommerce.multiSelect': 'multi-select',\n 'ecommerce.countHint': 'Click to adjust count (1-4)',\n 'ecommerce.refSettings': 'Reference images (optional)',\n 'ecommerce.styleTitle': 'Style / extra hints',\n 'ecommerce.protectedLabel': 'Features to preserve (optional)',\n 'ecommerce.assetsFull': 'Up to 4 product images',\n 'ecommerce.productRefTitle': 'Product images',\n 'ecommerce.productRefHint': 'Required · product shots, up to 4',\n 'ecommerce.styleRefTitle': 'Style reference',\n 'ecommerce.styleRefBadge': 'optional, 1 max',\n 'ecommerce.styleRefHint': 'Style, lighting, composition and mood only — the product itself cannot change.',\n 'ecommerce.styleRefUpload': 'Upload style reference',\n 'ecommerce.refInfoTitle': 'Product info',\n 'ecommerce.aiWrite': 'AI write',\n 'ecommerce.aiWriting': 'AI drafting…',\n 'ecommerce.aiWriteHint': 'Draft the product info prompt from name and category',\n 'ecommerce.promptInfoPlaceholder': 'Optional: brand & model, specs, materials, selling points, certifications, accessories, framing… or use AI write',\n 'ecommerce.optional': 'optional',\n 'ecommerce.previewBoardTitle': 'Pre-generation review',\n 'ecommerce.previewBoardHint': \"Tweak each image's prompt before submitting\",\n 'ecommerce.promptReset': 'Reset',\n 'ecommerce.promptAutoHint': 'Auto-generated · edit freely',\n 'ecommerce.refSelect': 'Reference image for this slot',\n 'ecommerce.refNone': 'No reference',\n 'ecommerce.role.product': 'Product',\n 'ecommerce.role.packaging': 'Packaging',\n 'ecommerce.role.detail': 'Detail / angle',\n 'ecommerce.role.style': 'Style',\n 'ecommerce.footerReady': '{count} images in this set',\n 'ecommerce.footerEmpty': 'Select the image set structure',\n 'ecommerce.results.title': 'Product set results',\n 'ecommerce.results.progress': '{done}/{total} completed',\n 'ecommerce.results.failed': '{count} failed',\n 'ecommerce.results.empty': 'After confirming, each image task and result will show here grouped by purpose',\n 'ecommerce.results.regenerate': 'Regenerate',\n 'ecommerce.results.export': 'Export manifest',\n 'ecommerce.results.newProduct': 'New product',\n 'prompt.placeholder': 'Describe the picture you want, e.g. an orange cat in an astronaut helmet raising a telescope on the moon, watercolor style, soft light…',\n 'prompt.required': 'Enter a prompt first',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': 'Over this model\\u2019s {limit}-character prompt limit; please shorten it',\n 'prompt.enhance': 'Enhance',\n 'prompt.enhancing': 'Enhancing…',\n 'prompt.enhanceHint': 'Expand the prompt with the configured chat model',\n 'prompt.configTitle': 'Configure a prompt enhancement model first',\n 'prompt.configHint': 'Settings has opened. Go to Plugins → AI Image, configure or reuse an API URL and key, choose a chat model, then save.',\n 'tasks.title': 'Generation tasks',\n 'tasks.queued': 'Queued',\n 'tasks.running': 'Generating',\n 'tasks.completed': 'Completed',\n 'tasks.failed': 'Failed',\n 'tasks.cancelled': 'Cancelled',\n 'tasks.cancel': 'Cancel',\n 'tasks.retry': 'Retry',\n 'params.size': 'Size',\n 'params.quality': 'Quality',\n 'params.count': 'Count',\n 'params.detail': 'Detail',\n 'size.auto': 'Auto',\n 'size.square': '1:1 Square',\n 'size.portrait34': '3:4 Portrait',\n 'size.landscape43': '4:3 Landscape',\n 'size.portrait916': '9:16 Vertical',\n 'size.portrait23': '2:3 Portrait',\n 'size.landscape32': '3:2 Landscape',\n 'size.wide169': '16:9 Widescreen',\n 'size.ultrawide21': '21:9 Ultrawide',\n 'quality.auto': 'Auto',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1',\n 'count.two': '2',\n 'count.three': '3',\n 'count.four': '4',\n 'detail.auto': 'Auto',\n 'detail.standard': 'Standard',\n 'detail.high': 'High',\n 'detail.hint': 'Passthrough parameter supported by some gpt-image-2 gateways; keep \"Auto\" for official endpoints',\n 'model.label': 'Model',\n 'model.channel': 'Channel',\n 'model.channelPlaceholder': 'Default channel',\n 'templates.favoritesCount': '{count} favorites',\n 'model.noEditModels': 'No model is available for this mode',\n 'compare.enable': 'Multi-model comparison',\n 'compare.models': 'Models to compare',\n 'compare.title': 'Multi-model comparison',\n 'compare.fullscreen': 'Fullscreen comparison',\n 'compare.selectRequired': 'Select at least one model',\n 'generate': 'Generate',\n 'generating': 'Generating…',\n 'edit.upload': 'Click or drag to upload a reference image',\n 'edit.uploadHint': 'PNG / JPG / WEBP, up to 10MB',\n 'edit.change': 'Change image',\n 'edit.remove': 'Remove',\n 'edit.required': 'Upload a reference image first',\n 'canvas.emptyTitle': 'Start creating',\n 'canvas.emptyHint': \"Double-click the canvas or use the bottom dock to add image/text nodes; wire image and text nodes into a target node, select it and generate from the composer below\",\n 'canvas.new': 'New creation',\n 'canvas.newHint': 'Start a new creation and clear the preview',\n 'canvas.error': 'Generation failed: {error}',\n 'canvas.submitting': 'Adding to the generation queue…',\n 'canvas.queued': 'Added to the generation queue',\n 'canvas.queueHint': '{count} task(s) waiting or generating',\n 'canvas.generating': 'Generating images…',\n 'canvas.elapsed': 'Elapsed {seconds}s',\n 'canvas.images': '{count} image(s) generated',\n 'download': 'Download',\n 'revisedPrompt': 'Refined prompt: {prompt}',\n 'history.title': 'History',\n 'history.empty': 'No history yet — generations will be saved here',\n 'history.clear': 'Clear',\n 'history.clearConfirm': 'Clear all history? This action cannot be undone.',\n 'history.restore': 'Restore',\n 'history.delete': 'Delete',\n 'history.images': 'images',\n 'history.viewing': 'History · {time}',\n 'history.search': 'Search prompt or model…',\n 'history.model': 'Model filter',\n 'history.ratio': 'Ratio filter',\n 'history.allModels': 'All models',\n 'history.allRatios': 'All ratios',\n 'gallery.title': 'Assets',\n 'gallery.categories': 'Categories',\n 'gallery.all': 'All assets',\n 'gallery.upload': 'Upload',\n 'gallery.uploadModel': 'Local upload',\n 'gallery.uploaded': 'Asset saved',\n 'gallery.uploadHint': 'Save local images for reuse on the canvas',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': 'Aspect ratio',\n 'gallery.tags': 'Tags',\n 'gallery.filterHint': 'Filter by mode, model, and aspect ratio',\n 'gallery.count': '· {count} works',\n 'gallery.viewMode': 'View mode',\n 'gallery.masonry': 'Masonry',\n 'gallery.grid': 'Grid',\n 'gallery.sort': 'Sort',\n 'gallery.newest': 'Newest',\n 'gallery.oldest': 'Oldest',\n 'gallery.untitled': 'Untitled work',\n 'gallery.search': 'Search works or models…',\n 'gallery.tagsPlaceholder': 'Tags, comma separated',\n 'gallery.tagsApply': 'Add tags',\n 'gallery.editTags': 'Edit tags',\n 'gallery.tagsEditShort': 'Edit',\n 'gallery.tagsSave': 'Save',\n 'gallery.tagsCancel': 'Cancel',\n 'gallery.selected': '{count} selected',\n 'gallery.selectionDone': 'Done selecting',\n 'gallery.selectionClear': 'Clear selection',\n 'gallery.downloadSelected': 'Download selected',\n 'gallery.exportJson': 'Export JSON',\n 'gallery.select': 'Select work',\n 'gallery.add': 'Add to gallery',\n 'gallery.added': 'Added to gallery',\n 'gallery.already': 'Already in gallery',\n 'gallery.delete': 'Remove',\n 'gallery.openFolder': 'Open folder',\n 'gallery.clear': 'Clear gallery',\n 'gallery.openFolderHint': 'Open the image save directory (~/.dsh/dsh-imagegen) in the system file manager',\n 'gallery.clearConfirm': 'Clear the entire gallery? This action cannot be undone.',\n 'gallery.empty': 'The gallery is empty — add images you like here',\n 'gallery.viewing': 'Gallery · {time}',\n 'preview.title': 'Image preview',\n 'preview.open': 'Click to preview',\n 'preview.close': 'Close',\n 'preview.prev': 'Previous',\n 'preview.next': 'Next',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': 'Image zoom controls',\n 'preview.zoomIn': 'Zoom in',\n 'preview.zoomOut': 'Zoom out',\n 'preview.zoomReset': 'Reset zoom',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': 'Copy prompt',\n 'preview.copied': 'Copied',\n 'preview.addToEdit': 'Add to image to image',\n 'config.missing': 'API not configured: open \"Settings → Plugins → Configurable\" and fill in api_url and api_key for AI Image.',\n 'config.generationTitle': 'Configure the image API first',\n 'config.generationHint': 'DSH Settings → Plugins → AI Image has opened. Enter the image API URL and API key, then save before generating.',\n 'config.enhancementTitle': 'Configure a prompt enhancement model first',\n 'config.enhancementHint': 'DSH Settings → Plugins → AI Image has opened. Configure or reuse a chat API URL and key, choose a chat model, then save.',\n 'config.disabledTitle': 'The AI Image plugin is disabled',\n 'config.disabledHint': 'DSH Settings → Plugins → AI Image has opened. Enable the plugin, then generate images.',\n 'config.configured': 'Connected to {url}',\n 'config.disabled': 'The plugin is disabled — re-enable it in Settings.',\n 'connection.connected': 'Connected',\n 'connection.disconnected': 'Disconnected',\n 'panel.collapseConfig': 'Collapse settings panel',\n 'panel.expandConfig': 'Expand settings panel',\n 'update.available': 'A new version is available: {version}',\n 'update.install': 'Update online',\n 'update.installing': 'Updating…',\n 'update.success': 'Updated to {version}; restart DSH to load it',\n 'update.failed': 'Update failed; please try again',\n 'update.release': 'View Release',\n 'settings.title': 'AI Image (dsh-imagegen)',\n 'settings.description': 'Configure the image generation API endpoint and key',\n 'settings.summary': 'Configure image-generation channels, models, and plugin switches',\n 'settings.currentVersion': 'Current version',\n 'settings.apiUrl': 'API URL (api_url)',\n 'settings.apiUrlHint': 'OpenAI-compatible base URL, e.g. https://api.openai.com/v1; /images/generations and /images/edits are appended',\n 'settings.apiKey': 'API Key (api_key)',\n 'settings.apiKeyHint': 'Bearer key, stored in plaintext in the local settings document; the UI only shows whether it is set',\n 'settings.apiKeySet': 'A key is stored; type a new value to replace it, or click \"Clear\" to remove it',\n 'settings.apiKeyClear': 'Clear',\n 'settings.imageModelsTitle': 'Image generation models',\n 'settings.imageModelsHint': 'Save the API URL and key, then detect candidate models. Select only models that actually support image generation.',\n 'settings.imageModels': 'Allowed image models',\n 'settings.imageModelsManualHint': 'One model per line. Add models manually when the API does not provide /models. The panel and Agent can use only this list.',\n 'settings.imageModelsFetch': 'Detect available models',\n 'settings.imageModelsLoading': 'Detecting…',\n 'settings.imageModelsCandidates': 'Detected candidate models (select, then save)',\n 'settings.addModel': '+ Add manually',\n 'settings.cancelAddModel': 'Hide add',\n 'settings.addModelPlaceholder': 'Enter a model name, e.g. qwen-image',\n 'settings.addModelConfirm': 'Add',\n 'settings.removeModel': 'Remove model',\n 'settings.optional': 'Optional',\n 'settings.moreOptions': 'More settings',\n 'settings.promptEnhanceTitle': 'Prompt enhancement model',\n 'settings.promptEnhanceHint': 'Expands short requests into complete image prompts. Blank URL and key reuse the image API configuration.',\n 'settings.promptApiUrl': 'Chat API URL (optional)',\n 'settings.promptApiUrlHint': 'OpenAI-compatible base URL. Leave blank to reuse the image API URL.',\n 'settings.promptApiKey': 'Chat API key (optional)',\n 'settings.promptApiKeyHint': 'Leave blank to reuse the image API key.',\n 'settings.promptModel': 'Chat model',\n 'settings.promptModelHint': 'Select or enter a model supporting /chat/completions.',\n 'settings.promptModelDetectionHint': 'Uses the image API by default. Detect and select one model.',\n 'settings.promptModelsFetch': 'Fetch available models',\n 'settings.promptModelsLoading': 'Fetching…',\n 'settings.promptModelsSelect': 'Select a model',\n 'settings.promptModelsCandidates': 'Detected chat model candidates',\n 'settings.addPromptModelPlaceholder': 'Enter a chat model name, e.g. gpt-4.1-mini',\n 'settings.promptApiAdvanced': 'Use a separate chat API (optional)',\n 'settings.announceToAgent': 'Announce this plugin to agents',\n 'settings.announceToAgentHint': 'When on, the plugin presence and capabilities are written into every agent system prompt',\n 'settings.allowAgentImageGeneration': 'Allow agents to generate images',\n 'settings.allowAgentImageGenerationHint': 'On by default. When off, agents cannot submit, query, or cancel image tasks; the sidebar studio remains available.',\n 'settings.enabled': 'Enable plugin',\n 'settings.enabledHint': 'When off, the generation studio is unavailable (this card stays available)',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving…',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved changes',\n 'settings.saveFailed': 'Save failed, please retry',\n 'settings.readOnly': 'The settings document is read-only; saving is disabled.',\n 'settings.notExposed': 'Settings namespace unavailable: this deployment does not serve this plugin\\'s settings.',\n 'settings.expand': 'Expand',\n 'settings.collapse': 'Collapse',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset',\n 'settings.invalidNumber': 'Enter a valid number',\n\n\n\n // template library\n 'templates.open': 'Templates',\n 'templates.title': 'Prompt Template Library',\n 'templates.sources': 'Template sources',\n 'templates.meta': '{count} templates · {origin}',\n 'templates.origin.bundled': 'bundled snapshot',\n 'templates.origin.refreshed': 'refreshed online',\n 'templates.search': 'Search template titles or prompts…',\n 'templates.all': 'All',\n 'templates.close': 'Close',\n 'templates.back': 'Back to list',\n 'templates.use': 'Use this prompt',\n 'templates.copy': 'Copy prompt',\n 'templates.copied': 'Copied',\n 'templates.refresh': 'Refresh this library',\n 'templates.refreshing': 'Refreshing…',\n 'templates.refreshed': 'Refreshed — {count} templates',\n 'templates.refreshFailed': 'Refresh failed: {error}',\n 'templates.favorites': 'Favorites',\n 'templates.favoritesHint': 'Show favorited templates only',\n 'templates.favoritesEmpty': 'No favorites yet — tap the star in a card\\'s top-right corner to save one',\n 'templates.favoriteAdd': 'Favorite this template',\n 'templates.favoriteRemove': 'Remove from favorites',\n 'templates.favorite': 'Favorite',\n 'templates.unfavorite': 'Favorited',\n 'templates.cacheAll': 'Cache all images',\n 'templates.cacheAllHint': 'Mirror every reference image of the current library to local disk through the host proxy, for offline browsing',\n 'templates.caching': 'Caching {done}/{total}…',\n 'templates.cached': 'All images cached',\n 'templates.empty': 'No matching templates',\n 'templates.loading': 'Loading the template library…',\n 'templates.loadFailed': 'Failed to load the library: {error}',\n 'templates.retry': 'Retry',\n 'templates.attribution': 'Templates and images come from each source site; author links are on each template',\n 'templates.source': 'Source: {label}',\n 'templates.featured': 'Featured',\n // inspiration wall (studio empty state)\n 'inspiration.title': 'Inspiration', 'inspiration.shuffle': 'Shuffle',\n 'inspiration.shuffling': 'Shuffling…',\n 'inspiration.useHint': 'Click to use this prompt',\n // channel management\n 'channels.title': 'Channels',\n 'channels.hint': 'Fill in each channel\\'s API URL and key to use its models',\n 'channels.empty': 'No channels yet — add one below',\n 'channels.addProvider': 'Add provider',\n 'channels.addCustom': 'Add custom channel',\n 'channels.untitled': 'Untitled channel',\n 'channels.keySet': 'Key set',\n 'channels.keyMissing': 'Key missing',\n 'channels.modelCount': '{n} model(s)',\n 'channels.noModels': 'No models',\n 'channels.defaultLabel': 'Default',\n 'channels.statusReady': 'Ready',\n 'channels.statusIncomplete': 'Config incomplete',\n 'channels.edit': 'Edit',\n 'channels.delete': 'Delete',\n 'channels.deleteConfirmTitle': 'Delete channel \"{name}\"? Its key will be removed; history and gallery records are kept.',\n 'channels.confirm': 'Delete',\n 'channels.cancel': 'Cancel',\n 'channels.editorTitle': 'Channel',\n 'channels.editorSaveNote': 'Changes take effect with the card\\'s \"Save\" button',\n 'channels.displayName': 'Display name',\n 'channels.apiUrl': 'API URL',\n 'channels.apiKey': 'API key',\n 'channels.keyReplaceHint': 'Configured — type a new value to replace it',\n 'channels.keyMissingHint': 'Enter a key',\n 'channels.keyClear': 'Clear key',\n 'channels.modelCatalogTitle': 'Model catalog',\n 'channels.noModelsHint': 'No models yet: run \"Detect\" to pull candidates, or add one manually; the alias (display name) defaults to the upstream model id and can be renamed.',\n 'channels.detect': 'Re-detect',\n 'channels.detecting': 'Detecting…',\n 'channels.detectSuccess': 'Connected ✓ · {n} candidates',\n 'channels.detectFailed': 'Detection failed: {error}',\n 'channels.detectOk': 'Connected to this API',\n 'channels.modelAliasLabel': 'Display name (alias)',\n 'channels.modelIdLabel': 'Upstream model id',\n 'channels.unknownProtocol': 'Unknown protocol — best-effort OpenAI',\n 'channels.removeModel': 'Remove model',\n 'channels.manualAddPlaceholder': 'Enter a model id, e.g. qwen-image',\n 'channels.addModelConfirm': 'Add',\n 'channels.copyFrom': 'Copy from another channel…',\n 'channels.copyApply': 'Copy',\n 'channels.candidatesTitle': 'Image model candidates (click to add)',\n 'channels.deleteThisChannel': 'Delete this channel',\n 'channels.setDefault': 'Set as default channel',\n 'channels.presetPickerTitle': 'Add provider',\n 'channels.presetPickerHint': 'Pick a built-in provider — only the API key is needed',\n 'channels.presetCustomHint': 'Configure the API URL, key, and model catalog yourself',\n 'channels.presetLoadFailed': 'Failed to load providers: {error}',\n 'canvas.skills.fileNode': 'File node',\n 'canvas.skills.addFileNode': 'Add file node',\n 'canvas.skills.emptyFileNode': 'Click to upload a file',\n 'canvas.skills.fileMenuUpload': 'Upload file',\n 'canvas.skills.fileDropHint': 'Drop a file here to upload',\n 'canvas.skills.fileDropSub': 'PDF / Office / text / images, up to 50MB',\n 'canvas.skills.fileSizeUnknown': 'Unknown size',\n 'canvas.skills.filePreviewUnavailable': 'No inline preview for this format — download it or run a skill',\n 'canvas.skills.filePreviewLoading': 'Loading preview…',\n 'canvas.skills.fileDownload': 'Download file',\n 'canvas.skills.fileOpen': 'Open with the system app',\n 'canvas.skills.fileKind.text': 'Text',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': 'Image',\n 'canvas.skills.fileKind.office': 'Document',\n 'canvas.skills.fileKind.archive': 'Archive',\n 'canvas.skills.fileKind.audio': 'Audio',\n 'canvas.skills.fileKind.video': 'Video',\n 'canvas.skills.fileKind.other': 'File',\n 'canvas.skills.fileTooLarge': 'The file exceeds the {size}MB limit',\n 'canvas.skills.fileTypeBlocked': '{ext} files cannot be uploaded for safety reasons',\n 'canvas.skills.fileUploadFailed': 'Upload failed: {message}',\n 'canvas.preview.expand': 'Open preview',\n 'canvas.preview.close': 'Close preview',\n 'canvas.preview.copy': 'Copy content',\n 'canvas.preview.copied': 'Copied',\n 'canvas.preview.openTab': 'Open in a new tab',\n 'canvas.preview.retry': 'Retry',\n 'canvas.preview.failed': 'Preview failed: {message}',\n 'canvas.preview.empty': 'The file is empty',\n 'canvas.preview.textTruncated': 'Large file — showing the first {chars} characters',\n 'canvas.preview.tableTruncated': 'Large table — showing the first {rows} rows',\n 'canvas.preview.archiveTruncated': 'Many entries — listing the first {count}',\n 'canvas.preview.docTruncated': 'Long document — showing the first {blocks} blocks',\n 'canvas.preview.slidesTruncated': 'Large deck — showing the first {count} slides',\n 'canvas.preview.slideOf': 'Slide {page}',\n 'canvas.preview.rendered': 'Rendered',\n 'canvas.preview.source': 'Source',\n 'canvas.preview.pdfFallback': 'This environment cannot display the PDF inline — download it instead',\n 'canvas.preview.pdfHeuristic': 'PDF text is extracted heuristically; complex layouts may lose their order',\n 'canvas.preview.reason.unsupported': 'No readable content for this format — download it, or turn it into an image or text node for a skill',\n 'canvas.preview.reason.unreadable': 'No text could be read from this file (a scan or an unusual encoding); convert it to an image node for a vision model',\n 'canvas.preview.doubleClick': 'Double-click to open the preview',\n 'canvas.skills.button': 'Skill',\n 'canvas.skills.menuTitle': 'Run a skill',\n 'canvas.skills.runOn': 'Run a skill on “{name}”',\n 'canvas.skills.pickerTitle': 'Pick a skill',\n 'canvas.skills.searchPlaceholder': 'Search skills…',\n 'canvas.skills.groupBuiltin': 'Built-in actions',\n 'canvas.skills.groupExternal': 'Local skills',\n 'canvas.skills.emptyExternal': 'No local skills found — install one under ~/.dsh/skills and reload',\n 'canvas.skills.empty': 'No skills available',\n 'canvas.skills.loading': 'Loading skills…',\n 'canvas.skills.tierLight': 'Quick',\n 'canvas.skills.tierHeavy': 'Long task',\n 'canvas.skills.accepts': 'Works on: {types}',\n 'canvas.skills.incompatible': 'This skill does not apply to this node type',\n 'canvas.skills.disabled': 'Canvas skills are disabled in the plugin settings',\n 'canvas.skills.noRegistry': 'This host mounted no skill registry (ctx.skills), so only built-in actions are available',\n 'canvas.skills.registryFailed': 'Could not read the skill list: {message}',\n 'canvas.skills.needAgent': 'Needs the host agent runtime, which this DSH build does not provide — long-task skills stay disabled',\n 'canvas.skills.unknown': 'That skill no longer exists',\n 'canvas.skills.canvasMissing': 'The canvas no longer exists — reload and try again',\n 'canvas.skills.noInput': 'This node has no processable input',\n 'canvas.skills.noChat': 'Quick skills need a chat model: Settings → Plugins → AI Image → Prompt enhancement',\n 'canvas.skills.noDescription': '(no description provided)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': 'You are running the skill “{name}”. Follow its instructions exactly on the input below, answer in the input language, and output only the result — never restate the instructions.',\n 'canvas.skills.externalHeader': 'Apply the skill “{name}” to the input below.',\n 'canvas.skills.externalHeavyCost': 'This is a long task: it runs a headless agent and may consume a large amount of model quota.',\n 'canvas.skills.extraInstruction': 'Additional requirement:',\n 'canvas.skills.inputsHeader': 'Input:',\n 'canvas.skills.inputsEmpty': '(no readable text — work from the attached images)',\n 'canvas.skills.needInstruction': 'Enter a custom polish instruction',\n 'canvas.skills.needImage': 'This skill needs an image node as input',\n 'canvas.skills.needText': 'This skill needs a text node with content as input',\n 'canvas.skills.fileMissing': 'The file asset is missing or was cleaned up',\n 'canvas.skills.extractFailed': 'Could not extract any content from this file',\n 'canvas.skills.nothingToRead': 'Nothing readable found',\n 'canvas.skills.truncated': 'Content was long and got truncated to {count} characters',\n 'canvas.skills.tooManyImages': 'More than {count} images were selected; only the first ones were processed',\n 'canvas.skills.extractNodeTitle': 'Content of “{name}”',\n 'canvas.skills.summaryHead': 'Collected {count} source nodes:',\n 'canvas.skills.describeAsk': 'Describe these {count} image(s).',\n 'canvas.skills.polishNodeTitle': 'Polished ({style})',\n 'canvas.skills.skillMissing': 'The skill “{name}” is not installed. Install it first (for example, ask your DSH agent to install https://github.com/ningzimu/image-to-editable-ppt-skill) and retry.',\n 'canvas.skills.agentFailed': 'Could not create a headless agent — check the model and agent preset configuration',\n 'canvas.skills.needModel': 'This host publishes no default model (agent-default-model), so a long-task skill cannot start its agent. Pick a default model in Settings → General and retry.',\n 'canvas.skills.noOutputFile': 'The skill produced no file; the agent’s answer was saved as a text node',\n 'canvas.skills.heavyIntro': 'You are running the skill “{name}” for a visual canvas. Complete the whole procedure exactly as documented; do not skip steps.',\n 'canvas.skills.heavyBodyHeader': 'Skill instructions:',\n 'canvas.skills.heavyInputsHeader': 'Input files for this task (absolute paths):',\n 'canvas.skills.heavyOutputHeader': 'Write the final artifact(s) into this directory: {dir}',\n 'canvas.skills.heavyRules': 'Requirements:\\n1. Keep every intermediate file in the working directory; the final artifact may only go into the output directory above.\\n2. When done, reply briefly with the artifact file names, whether it succeeded, and any caveats.',\n 'canvas.skills.heavyAsk': 'Run the skill “{name}”.',\n 'canvas.skills.heavyAskInputs': 'Input directory: {dir}',\n 'canvas.skills.heavyAskOutput': 'Output directory: {dir}',\n 'canvas.skills.heavyAskLoadSkill': '(If the skill instructions are not in context, load them by the skill name “{name}” first.)',\n 'canvas.skills.stageQueued': 'Queued',\n 'canvas.skills.stagePreparing': 'Preparing input files',\n 'canvas.skills.stageReading': 'Reading node content',\n 'canvas.skills.stageRunning': 'Running the skill',\n 'canvas.skills.stageCollecting': 'Collecting artifacts',\n 'canvas.skills.stageDone': 'Done',\n 'canvas.skills.stageCancelled': 'Cancelled',\n 'canvas.skills.stagePrepared': 'Prepared {count} input file(s)',\n 'canvas.skills.stageArtifacts': 'Running — {count} file(s) produced',\n 'canvas.skills.runElapsed': 'Elapsed',\n 'canvas.skills.runRetry': 'Retry',\n 'canvas.skills.runStarted': 'Skill “{name}” started',\n 'canvas.skills.runDone': 'Skill finished — {count} new node(s)',\n 'canvas.skills.runFailed': 'The skill failed: {message}',\n 'canvas.skills.runCancelled': 'The skill run was cancelled',\n 'canvas.skills.cancel': 'Cancel run',\n 'canvas.skills.cancelling': 'Cancelling…',\n 'canvas.skills.confirmTitle': 'Run a long-task skill',\n 'canvas.skills.confirmBody': '“{name}” starts a headless agent on this machine. It can run for minutes to tens of minutes and consume a large amount of model quota.',\n 'canvas.skills.confirmCost': 'Cost: {hint}',\n 'canvas.skills.confirmRequires': 'Requires: {list}',\n 'canvas.skills.confirmRun': 'Run it',\n 'canvas.skills.confirmCancel': 'Not now',\n 'canvas.skills.batchTitle': 'Batch run',\n 'canvas.skills.batchNote': 'Runs once per node, one after another ({count} nodes)',\n 'canvas.skills.batchTooMany': 'At most {count} nodes per run',\n 'canvas.skills.sourceBadge': 'Skill output',\n 'canvas.skills.sourceTip': 'Produced by the skill “{name}” from {count} source node(s)',\n 'canvas.skills.rerun': 'Run the same skill again',\n 'canvas.skills.history': 'Skill run history',\n 'canvas.skills.historyEmpty': 'No runs yet',\n 'canvas.skills.needConfirm': 'This skill needs confirmation before it runs',\n 'canvas.skills.polish': 'Polish text',\n 'canvas.skills.polishDesc': 'Rewrite a text node in a chosen style: more formal / casual / shorter / expanded, or a custom instruction',\n 'canvas.skills.describe': 'Describe image',\n 'canvas.skills.describeDesc': 'Read an image with a vision model and produce a text node (usable as a prompt)',\n 'canvas.skills.extract': 'Extract content',\n 'canvas.skills.extractDesc': 'Pull the text out of a file or image into a text node (text / code / OOXML / PDF)',\n 'canvas.skills.ppt': 'Image → editable PPT',\n 'canvas.skills.pptDesc': 'Drive the local image-to-editable-ppt skill to rebuild images into an editable .pptx file node',\n 'canvas.skills.pptCost': 'Long task: a 10-page deck can consume a large quota; a single page often takes 10+ minutes',\n 'canvas.skills.pptRequires': 'Needs the image-to-editable-ppt skill installed locally, with the OCR token and image backend configured as its docs describe',\n 'canvas.polish.button': 'AI polish',\n 'canvas.polish.title': 'AI polish',\n 'canvas.polish.formal': 'More formal',\n 'canvas.polish.casual': 'More casual',\n 'canvas.polish.shorter': 'Shorter',\n 'canvas.polish.expand': 'Expand',\n 'canvas.polish.custom': 'Custom',\n 'canvas.polish.customPlaceholder': 'e.g. rewrite as a social post with emoji and hashtags',\n 'canvas.polish.run': 'Polish',\n 'canvas.polish.previewTitle': 'Before / after',\n 'canvas.polish.apply': 'Apply',\n 'canvas.polish.revert': 'Revert',\n 'canvas.polish.applied': 'Polish applied',\n 'canvas.polish.directHint': 'Current setting: polish overwrites the node directly (switch to preview-first in the settings)',\n 'settings.skillsEnabled': 'Canvas skills',\n 'canvas.skills.libraryButton': 'Skill library',\n 'canvas.skills.libraryTitle': 'Skill library',\n 'canvas.skills.libraryInstalled': 'Installed skills ({count})',\n 'canvas.skills.libraryEmpty': 'No skills installed yet. Install one from a repository URL, or upload a skill archive.',\n 'canvas.skills.libraryRoot': 'Install root: {root}',\n 'canvas.skills.libraryInstall': 'Install from URL',\n 'canvas.skills.libraryInstallHint': 'GitHub repo / folder / single SKILL.md, any git URL, or a zip URL; separate several with new lines. The host loads installs live — no restart.',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo or …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': 'Upload archive',\n 'canvas.skills.libraryUploadHint': 'Upload a zip that contains SKILL.md (≤50MB); it is unpacked into ~/.dsh/skills.',\n 'canvas.skills.libraryDropzone': 'Click or drop a .zip archive here',\n 'canvas.skills.libraryForce': 'Replace an installed skill of the same name',\n 'canvas.skills.libraryInstallAction': 'Install',\n 'canvas.skills.libraryInstalling': 'Installing…',\n 'canvas.skills.libraryRemove': 'Uninstall',\n 'canvas.skills.libraryRemoving': 'Uninstalling…',\n 'canvas.skills.libraryRemoveConfirm': 'Uninstall “{name}”? Its folder is deleted from the skill library.',\n 'canvas.skills.libraryInstalledToast': 'Installed: {names}',\n 'canvas.skills.libraryFailedToast': 'Install failed: {message}',\n 'canvas.skills.libraryRemovedToast': 'Uninstalled: {name}',\n 'canvas.skills.libraryUnavailable': 'This host cannot manage the skill library',\n 'canvas.skills.libraryReload': 'Refresh',\n 'canvas.skills.libraryOpenFolder': 'Open skill folder',\n 'canvas.skills.installNow': 'Install',\n 'canvas.skills.needsInstall': 'Not installed',\n 'canvas.skills.missingHint': '“{name}” is not installed yet — install it to use this action',\n 'canvas.skills.installArchiveFailed': 'Archive install failed: {message}',\n 'canvas.skills.libraryIssueFrontmatter': 'No YAML frontmatter (name / description are required), so the host skill registry never loads it and no node can run it',\n 'canvas.skills.libraryIssueName': 'The frontmatter name “{name}” is not kebab-case (lowercase letters, digits and hyphens), so the host skill registry ignores it and no node can run it',\n 'canvas.skills.libraryIssueDescription': 'The SKILL.md frontmatter has no description (required), so the host skill registry never loads it and no node can run it',\n 'canvas.skills.libraryIssueInvocation': 'The disable-model-invocation / user-invocable spelling cannot be parsed, so the host skill registry ignores it and no node can run it',\n 'canvas.skills.libraryIssueUserOnly': 'This skill declares disable-model-invocation: it is human-invocable only, so canvas nodes cannot run it',\n 'canvas.skills.configOpen': 'Configure',\n 'canvas.skills.configSave': 'Save',\n 'canvas.skills.configSaveApply': 'Save and apply',\n 'canvas.skills.configSaving': 'Saving…',\n 'canvas.skills.configApplying': 'Applying…',\n 'canvas.skills.configSaved': 'Saved',\n 'canvas.skills.configSaveFailed': 'Could not save',\n 'canvas.skills.configApplied': 'Saved and applied',\n 'canvas.skills.configApplyFailed': 'Could not apply',\n 'canvas.skills.configClear': 'Clear',\n 'canvas.skills.configSecretSet': 'Set (leave empty to keep it)',\n 'canvas.skills.configSecretUnset': 'Not set',\n 'canvas.skills.configMissingHint': 'Needs configuration: {fields}',\n 'canvas.skills.configGo': 'Configure',\n 'canvas.skills.configSourceSkill': 'These fields are declared by the skill’s own skill.config.json',\n 'canvas.skills.configSourcePlugin': 'These fields come from this plugin’s built-in recipe for a known skill (a skill’s own declaration wins)',\n 'canvas.skills.configHeader': 'Already configured:',\n 'canvas.skills.configUnwritable': 'This host cannot write settings, so skill configuration is unavailable',\n 'canvas.skills.configIssueVersion': 'skill.config.json declares a version other than 1, so the declaration was ignored',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json could not be parsed (JSON syntax error), so the declaration was ignored',\n 'canvas.skills.configIssueEmpty': 'skill.config.json declares no usable field or step, so it was ignored',\n}\n\n/** Locale key union. */\nexport type ImageGenKey = keyof typeof zh\n\n/** Russian mirror of every key (полный словарь интерфейса). */\nexport const ru: Record<keyof typeof zh, string> = {\n 'entry.label': 'ИИ-генерация',\n 'entry.tooltip': 'Панель ИИ-генерации изображений (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream)',\n 'entry.newSession': 'Новая сессия',\n 'entry.newSessionTooltip': 'Создать новую сессию DSH',\n 'entry.image': 'Генерация',\n 'panel.title': 'VisioWork',\n 'panel.githubTip': 'Понравился плагин или есть идеи? Поставьте звезду или откройте issue на GitHub!',\n 'conversation.add': 'В диалог',\n 'conversation.adding': 'Добавляем…',\n 'conversation.added': 'Изображение добавлено в текущий диалог',\n 'conversation.addHint': 'После добавления можно править через /edit_image; команда использует модель плагина',\n 'conversation.noSession': 'Сначала откройте сессию, затем добавьте изображение',\n 'conversation.unavailable': 'Текущая сессия недоступна',\n 'conversation.busy': 'Диалог отправляет сообщение, попробуйте позже',\n 'mode.text': 'Из текста',\n 'mode.edit': 'Из изображения',\n 'workspace.label': 'Режим работы',\n 'workspace.normal': 'Генерация',\n 'workspace.canvas': 'Бесконечный холст',\n 'workspace.ecommerce': 'E-commerce',\n 'canvas.addImage': 'Добавить изображение',\n 'canvas.addText': 'Добавить текст',\n 'canvas.generate': 'Создать изображение',\n 'canvas.addToCanvas': 'Добавить на холст',\n 'canvas.untitled': 'Безымянный холст',\n 'canvas.imageNode': 'Узел изображения',\n 'canvas.textNode': 'Текстовый узел',\n 'canvas.fromGallery': 'Из галереи',\n 'canvas.fromHistory': 'Из истории',\n 'canvas.project': 'Проект холста',\n 'canvas.newCanvas': 'Новый холст',\n 'canvas.deleteCanvas': 'Удалить холст',\n 'canvas.deleteCanvasConfirm': 'Нажмите ещё раз для подтверждения',\n 'canvas.rename': 'Переименовать холст',\n 'canvas.renameHint': 'Двойной клик — переименовать',\n 'canvas.background': 'Фон холста',\n 'canvas.backgroundDots': 'Точки',\n 'canvas.backgroundLines': 'Сетка',\n 'canvas.backgroundWaves': 'Волны',\n 'canvas.backgroundShapeGrid': 'Бегущая сетка',\n 'canvas.backgroundDotField': 'Поле точек',\n 'canvas.backgroundDotGrid': 'Интерактивные точки',\n 'canvas.backgroundFloatingLines': 'Плавающие линии',\n 'canvas.backgroundFlow': 'Жидкостный поток',\n 'canvas.backgroundLiquid': 'Жидкий эфир',\n 'canvas.backgroundFaultyTerminal': 'Сбойный терминал',\n 'canvas.backgroundSilk': 'Шёлк',\n 'canvas.backgroundGalaxy': 'Галактика',\n 'canvas.backgroundBlank': 'Пустой',\n 'canvas.backgroundUpload': 'Загрузить свой фон…',\n 'canvas.backgroundRemove': 'Убрать свой фон',\n 'canvas.templateLibrary': 'Библиотека шаблонов (выберите промпт — создадутся текстовый узел и узел конфигурации)',\n 'canvas.undo': 'Отменить (Ctrl+Z)',\n 'canvas.redo': 'Повторить (Ctrl+Shift+Z)',\n 'canvas.saving': 'Сохранение…',\n 'canvas.saved': 'Сохранено',\n 'canvas.saveFailed': 'Ошибка сохранения',\n 'canvas.loading': 'Загрузка…',\n 'canvas.toolSelect': 'Выбор / перемещение узлов',\n 'canvas.toolPan': 'Перетаскивание холста (Space / Ctrl)',\n 'canvas.delete': 'Удалить',\n 'canvas.duplicate': 'Дублировать (Ctrl+D)',\n 'canvas.download': 'Скачать изображение',\n 'canvas.fitView': 'Показать всё содержимое',\n 'canvas.minimapOpen': 'Открыть мини-карту',\n 'canvas.minimapClose': 'Скрыть мини-карту',\n 'canvas.minimap': 'Мини-карта холста',\n 'canvas.zoom': 'Масштаб',\n 'canvas.textPlaceholder': 'Введите текст…',\n 'canvas.generatingNode': 'Генерация…',\n 'canvas.generateFailed': 'Не удалось создать изображение',\n 'canvas.retry': 'Повторить',\n 'canvas.taskLost': 'Задача генерации потеряна (сервис перезапущен); повторите',\n 'canvas.emptyImageNode': 'Нажмите, чтобы загрузить изображение',\n 'canvas.connectHint': 'Потяните к другому узлу, чтобы соединить',\n 'canvas.connectAddHint': 'Потяните к другому узлу, чтобы соединить; клик добавляет следующий узел',\n 'canvas.resizeHint': 'Потяните, чтобы изменить размер (пропорции зафиксированы)',\n 'canvas.count': 'Кол-во изображений',\n 'canvas.configNode': 'Конфигурация генерации',\n 'canvas.addConfigNode': 'Добавить узел конфигурации',\n 'canvas.configHint': 'Подключите узлы изображений слева как референсы и текстовые узлы как промпт; выберите этот узел и генерируйте через панель ниже.',\n 'canvas.composerLinked': 'Подключено узлов ввода: {count}',\n 'canvas.composerPlaceholder': 'Опишите изображение; либо подключите текстовые узлы как промпт…',\n 'canvas.countUnit': '{count} шт.',\n 'canvas.model': 'Модель изображений',\n 'canvas.modelPlaceholder': 'Выберите модель',\n 'canvas.size': 'Пропорции',\n 'canvas.sizeAuto': 'Авто',\n 'canvas.quality': 'Качество',\n 'canvas.qualityAuto': 'Авто',\n 'canvas.needApi': 'Сначала настройте API генерации',\n 'canvas.needPrompt': 'Введите описание генерации',\n 'canvas.needModel': 'Нет доступной модели изображений',\n 'canvas.openSettings': 'Открыть настройки',\n 'canvas.deleteConnection': 'Удалить связь',\n 'canvas.paste': 'Вставить (Ctrl+V)',\n 'canvas.addImageNode': 'Добавить узел изображения',\n 'canvas.addTextNode': 'Добавить текстовый узел',\n 'canvas.addSketch': 'Добавить скетчборд (эскиз → изображение)',\n 'canvas.sketchNode': 'Скетчборд',\n 'canvas.addSketchNode': 'Добавить узел скетчборда',\n 'canvas.sketchPlaceholder': 'Нарисуйте эскиз от руки и подключите к узлу конфигурации как референс',\n 'canvas.sketchBrush': 'Кисть',\n 'canvas.sketchEraser': 'Ластик',\n 'canvas.sketchUndo': 'Отменить последний штрих',\n 'canvas.sketchClear': 'Очистить доску',\n 'canvas.sketchWidth': 'Толщина линии',\n 'canvas.dismiss': 'Закрыть',\n 'canvas.annotate': 'Разметка: обведите область, чтобы прикрепить карточку промпта',\n 'canvas.annotationNode': 'Промпт разметки',\n 'canvas.annotationTag': 'Разметка',\n 'canvas.annotationPlaceholder': 'Опишите, что должно быть в рамке, например: заменить на рыжего кота',\n 'canvas.annotationDrawHint': 'Проведите по изображению, чтобы нарисовать рамку; после отпускания появится карточка промпта, которая следует за изображением (соединять не нужно)',\n 'canvas.removeBackground': 'Удалить фон (локальная обработка, прозрачный PNG)',\n 'canvas.removingBackground': 'Удаляем фон…',\n 'canvas.removeBgDone': 'Фон удалён (прозрачных пикселей {percent}%)',\n 'canvas.removeBgFailed': 'Фон слишком сложный для локальной обработки; обведите объект разметкой и попросите модель перерисовать',\n 'canvas.alreadyTransparent': 'У этого изображения уже прозрачный фон',\n 'canvas.noBackgroundNode': 'Изображение без фона',\n 'canvas.splitLayers': 'Разделить слои (ИИ-разбор на редактируемые слои)',\n 'canvas.splittingLayers': 'Разделяем слои…',\n 'canvas.layerSplitDone': 'Разделено на {count} слоёв',\n 'canvas.layerSplitEmpty': 'Модель не нашла слоёв для разделения',\n 'canvas.layerBackground': 'Фон · {label}',\n 'canvas.layerObject': 'Слой · {label}',\n 'canvas.layerText': 'Текст · {label}',\n 'canvas.layerKindBackground': 'Фон',\n 'canvas.layerKindObject': 'Элемент',\n 'canvas.layerKindText': 'Текст',\n 'canvas.nodeModel': 'Модель для этого узла (повтор и подключённые узлы конфигурации)',\n 'canvas.modelDefault': 'Модель по умолчанию',\n 'canvas.fontSmaller': 'Меньше шрифт',\n 'canvas.fontLarger': 'Больше шрифт',\n 'canvas.bold': 'Жирный',\n 'canvas.textColor': 'Цвет текста',\n 'canvas.colorDefault': 'Как в теме',\n 'canvas.close': 'Закрыть',\n 'canvas.tabUpload': 'Загрузка',\n 'canvas.tabHistory': 'История',\n 'canvas.tabGallery': 'Материалы',\n 'canvas.imageMenuUpload': 'Локальное изображение',\n 'canvas.imageMenuAssets': 'Материалы',\n 'canvas.imageMenuHistory': 'История',\n 'canvas.imageMenuGenerate': 'AI-генерация',\n 'canvas.tabGenerate': 'AI-генерация',\n 'canvas.dropHint': 'Перетащите изображения сюда',\n 'canvas.dropSub': 'Несколько файлов, PNG / JPG / WebP / GIF',\n 'canvas.untitledWork': 'Без названия',\n 'canvas.picked': 'Выбрано: {count}',\n 'canvas.generateAndAdd': 'Создать и добавить на холст',\n 'chat.expand': 'Показать диалог',\n 'chat.collapse': 'Скрыть диалог',\n 'chat.toggle': 'Диалог',\n 'config.resizeHint': 'Потяните, чтобы изменить ширину панели параметров',\n 'ecommerce.generation': 'Параметры генерации',\n 'ecommerce.title': 'Режим e-commerce',\n 'ecommerce.badge': 'Превью',\n 'ecommerce.short': 'E-com',\n 'ecommerce.anchorPending': 'Главное изображение готово, остальные генерируются по нему для единого стиля…',\n 'ecommerce.anchorFailed': 'Не удалось создать главное изображение, остальные приостановлены. Повторите главное (или всю группу) и продолжите.',\n 'ecommerce.anchorNote': 'Якорная генерация: сначала главное изображение, остальные создаются по нему для единого стиля',\n 'ecommerce.noAssetWarn': 'Товар не загружен: главное изображение будет создано по описанию и станет якорем; загрузите фото товара для большей точности',\n 'ecommerce.product': 'Товар',\n 'ecommerce.productName': 'Название товара (обязательно)',\n 'ecommerce.projectName': 'Название проекта (необязательно)',\n 'ecommerce.constraints': 'Преимущества и ограничения',\n 'ecommerce.sellingPoints': 'Доп. информация: бренд и модель, характеристики (объём/размер/вес), материалы, преимущества, сертификация, комплектация…',\n 'ecommerce.protectedFeatures': 'Что обязательно сохранить: цвет, логотип, текст упаковки, конструкцию',\n 'ecommerce.styleHint': 'Стиль, например: премиальный / реалистичный / естественный свет',\n 'ecommerce.setStructure': 'Структура комплекта',\n 'ecommerce.preview': 'Создать превью комплекта',\n 'ecommerce.planTitle': 'Будет сгенерировано изображений: {count}',\n 'ecommerce.planSlot': '{count} шт. · {description}',\n 'ecommerce.confirm': 'Сгенерировать весь комплект',\n 'ecommerce.uploadRef': 'Фото товара (обязательно, до 4 шт.: товар / упаковка / детали)',\n 'ecommerce.uploadShort': 'Загрузить материалы',\n 'ecommerce.sellingTitle': 'Преимущества товара',\n 'ecommerce.advanced': 'Дополнительно (площадка / ограничения)',\n 'ecommerce.params': 'Параметры',\n 'ecommerce.platformLabel': 'Площадка',\n 'ecommerce.languageLabel': 'Язык текста',\n 'ecommerce.customLanguageOption': 'Свой язык…',\n 'ecommerce.customLanguageLabel': 'Свой язык',\n 'ecommerce.customLanguagePlaceholder': 'Введите язык, например: Русский / 日本語 / Português',\n 'ecommerce.ratioLabel': 'Пропорции',\n 'ecommerce.categoryLabel': 'Категория',\n 'ecommerce.multiSelect': 'Можно выбрать несколько',\n 'ecommerce.countHint': 'Нажмите, чтобы изменить количество (1-4)',\n 'ecommerce.refSettings': 'Настройки референсов (необязательно)',\n 'ecommerce.styleTitle': 'Стиль / дополнения',\n 'ecommerce.protectedLabel': 'Обязательно сохранить (необязательно)',\n 'ecommerce.assetsFull': 'Не более 4 фото товара',\n 'ecommerce.productRefTitle': 'Референсы товара',\n 'ecommerce.productRefHint': 'Обязательно · фото товара, до 4 шт.',\n 'ecommerce.styleRefTitle': 'Референс стиля',\n 'ecommerce.styleRefBadge': 'опционально, 1 шт.',\n 'ecommerce.styleRefHint': 'Только стиль, свет, композиция и настроение — сам товар заменить нельзя.',\n 'ecommerce.styleRefUpload': 'Загрузить референс стиля',\n 'ecommerce.refInfoTitle': 'Параметры товара',\n 'ecommerce.aiWrite': 'ИИ-заполнить',\n 'ecommerce.aiWriting': 'ИИ составляет…',\n 'ecommerce.aiWriteHint': 'Составить параметры товара из названия и категории',\n 'ecommerce.promptInfoPlaceholder': 'Опционально: бренд и модель, характеристики, материалы, преимущества, сертификация, комплектация… или ИИ-заполнить',\n 'ecommerce.optional': 'опционально',\n 'ecommerce.previewBoardTitle': 'Предпросмотр перед генерацией',\n 'ecommerce.previewBoardHint': 'Промпт каждого изображения можно изменить до запуска',\n 'ecommerce.promptReset': 'Сбросить',\n 'ecommerce.promptAutoHint': 'Автоматически · можно изменить',\n 'ecommerce.refSelect': 'Референс для этого назначения',\n 'ecommerce.refNone': 'Без референса',\n 'ecommerce.role.product': 'Товар',\n 'ecommerce.role.packaging': 'Упаковка',\n 'ecommerce.role.detail': 'Детали/ракурс',\n 'ecommerce.role.style': 'Стиль',\n 'ecommerce.footerReady': 'Будет сгенерировано {count} изображений',\n 'ecommerce.footerEmpty': 'Выберите структуру комплекта',\n 'ecommerce.results.title': 'Результаты комплекта',\n 'ecommerce.results.progress': '{done}/{total} готово',\n 'ecommerce.results.failed': '{count} с ошибкой',\n 'ecommerce.results.empty': 'После запуска генерации здесь появятся статус и результат каждого изображения',\n 'ecommerce.results.regenerate': 'Сгенерировать заново',\n 'ecommerce.results.export': 'Экспорт списка',\n 'ecommerce.results.newProduct': 'Новый товар',\n 'prompt.placeholder': 'Опишите желаемое изображение, например: рыжий кот в шлеме космонавта смотрит в телескоп на Луне, акварель, мягкий свет…',\n 'prompt.required': 'Введите промпт',\n 'prompt.count': '{count}',\n 'prompt.countLimit': '{count} / {limit}',\n 'prompt.overLimit': 'Превышен лимит промпта для этой модели ({limit} символов); сократите текст',\n 'prompt.enhance': 'Улучш.',\n 'prompt.enhancing': 'Улучшаем…',\n 'prompt.enhanceHint': 'Развернуть краткий промпт через настроенную диалоговую модель',\n 'prompt.configTitle': 'Сначала настройте модель улучшения промптов',\n 'prompt.configHint': 'Настройки открыты. Перейдите в «Плагины → ИИ-генерация», укажите или переиспользуйте API-адрес и ключ, выберите диалоговую модель и сохраните.',\n 'tasks.title': 'Задачи генерации',\n 'tasks.queued': 'В очереди',\n 'tasks.running': 'Генерация',\n 'tasks.completed': 'Готово',\n 'tasks.failed': 'Ошибка',\n 'tasks.cancelled': 'Отменено',\n 'tasks.cancel': 'Отменить',\n 'tasks.retry': 'Повторить',\n 'params.size': 'Размер',\n 'params.quality': 'Чёткость',\n 'params.count': 'Количество',\n 'params.detail': 'Детализация',\n 'size.auto': 'Авто',\n 'size.square': '1:1 квадрат',\n 'size.portrait34': '3:4 верт.',\n 'size.landscape43': '4:3 гориз.',\n 'size.portrait916': '9:16 верт.',\n 'size.portrait23': '2:3 верт.',\n 'size.landscape32': '3:2 гориз.',\n 'size.wide169': '16:9 шир.',\n 'size.ultrawide21': '21:9 ультрашир.',\n 'quality.auto': 'Авто',\n 'quality.1k': '1K',\n 'quality.2k': '2K',\n 'quality.4k': '4K',\n 'count.one': '1 шт.',\n 'count.two': '2 шт.',\n 'count.three': '3 шт.',\n 'count.four': '4 шт.',\n 'detail.auto': 'Авто',\n 'detail.standard': 'Стандарт',\n 'detail.high': 'Высокая',\n 'detail.hint': 'Сквозной параметр, поддерживают некоторые шлюзы gpt-image-2; для официального API оставьте «Авто»',\n 'model.label': 'Модель',\n 'model.channel': 'Канал',\n 'model.channelPlaceholder': 'Канал по умолчанию',\n 'templates.favoritesCount': 'В избранном: {count}',\n 'model.noEditModels': 'В текущем режиме нет доступных моделей',\n 'compare.enable': 'Сравнение моделей',\n 'compare.models': 'Модели для сравнения',\n 'compare.title': 'Сравнение результатов моделей',\n 'compare.fullscreen': 'Полноэкранное сравнение',\n 'compare.selectRequired': 'Выберите хотя бы одну модель для сравнения',\n 'generate': 'Сгенерировать',\n 'generating': 'Генерация…',\n 'edit.upload': 'Нажмите или перетащите референс',\n 'edit.uploadHint': 'PNG / JPG / WEBP, до 10 МБ',\n 'edit.change': 'Заменить изображение',\n 'edit.remove': 'Убрать',\n 'edit.required': 'Сначала загрузите референс',\n 'canvas.emptyTitle': 'Начните творить',\n 'canvas.emptyHint': 'Двойной клик или нижняя панель добавляют узлы; соедините узлы изображений и текста с целевым узлом, выберите его и генерируйте внизу',\n 'canvas.new': 'Новая генерация',\n 'canvas.newHint': 'Начать новую генерацию и очистить просмотр',\n 'canvas.error': 'Ошибка генерации: {error}',\n 'canvas.submitting': 'Ставим в очередь…',\n 'canvas.queued': 'Задача в очереди',\n 'canvas.queueHint': 'Сейчас ожидают {count} задач',\n 'canvas.generating': 'Генерируем изображение…',\n 'canvas.elapsed': 'Прошло {seconds} с',\n 'canvas.images': 'Сгенерировано: {count}',\n 'download': 'Скачать',\n 'revisedPrompt': 'Улучшенный промпт: {prompt}',\n 'history.title': 'История',\n 'history.empty': 'Истории пока нет — созданные изображения появятся здесь',\n 'history.clear': 'Очистить',\n 'history.clearConfirm': 'Очистить всю историю? Действие необратимо.',\n 'history.restore': 'Восстановить',\n 'history.delete': 'Удалить',\n 'history.images': 'шт.',\n 'history.viewing': 'История · {time}',\n 'history.search': 'Поиск по промптам и моделям…',\n 'history.model': 'Фильтр по модели',\n 'history.ratio': 'Фильтр по пропорциям',\n 'history.allModels': 'Все модели',\n 'history.allRatios': 'Все пропорции',\n 'gallery.title': 'Материалы',\n 'gallery.categories': 'Категории',\n 'gallery.all': 'Все материалы',\n 'gallery.upload': 'Загрузить',\n 'gallery.uploadModel': 'Локальный файл',\n 'gallery.uploaded': 'Материал добавлен',\n 'gallery.uploadHint': 'Сохраните локальные изображения для холста',\n 'gallery.gpt': 'gpt-image-2',\n 'gallery.grok': 'grok-imagine-image',\n 'gallery.ratio': 'Пропорции',\n 'gallery.tags': 'Метки',\n 'gallery.filterHint': 'Фильтр галереи по режиму, модели и пропорциям',\n 'gallery.count': '· всего {count}',\n 'gallery.viewMode': 'Вид',\n 'gallery.masonry': 'Плитка',\n 'gallery.grid': 'Сетка',\n 'gallery.sort': 'Сортировка',\n 'gallery.newest': 'Сначала новые',\n 'gallery.oldest': 'Сначала старые',\n 'gallery.untitled': 'Без названия',\n 'gallery.search': 'Поиск по работам и моделям…',\n 'gallery.tagsPlaceholder': 'Метки через запятую',\n 'gallery.tagsApply': 'Добавить метки',\n 'gallery.editTags': 'Править метки',\n 'gallery.tagsEditShort': 'Правка',\n 'gallery.tagsSave': 'Сохранить',\n 'gallery.tagsCancel': 'Отмена',\n 'gallery.selected': 'Выбрано: {count}',\n 'gallery.selectionDone': 'Завершить выбор',\n 'gallery.selectionClear': 'Снять выбор',\n 'gallery.downloadSelected': 'Скачать выбранные',\n 'gallery.exportJson': 'Экспорт JSON',\n 'gallery.select': 'Выбрать работы',\n 'gallery.add': 'В галерею',\n 'gallery.added': 'Добавлено в галерею',\n 'gallery.already': 'Уже в галерее',\n 'gallery.delete': 'Убрать из галереи',\n 'gallery.openFolder': 'Открыть папку',\n 'gallery.clear': 'Очистить галерею',\n 'gallery.openFolderHint': 'Открыть каталог сохранения изображений (~/.dsh/dsh-imagegen) в проводнике',\n 'gallery.clearConfirm': 'Очистить всю галерею? Действие необратимо.',\n 'gallery.empty': 'Галерея пуста — добавьте понравившиеся изображения',\n 'gallery.viewing': 'Галерея · {time}',\n 'preview.title': 'Просмотр изображения',\n 'preview.open': 'Открыть просмотр',\n 'preview.close': 'Закрыть',\n 'preview.prev': 'Назад',\n 'preview.next': 'Вперёд',\n 'preview.index': '{index} / {total}',\n 'preview.zoomControls': 'Управление масштабом',\n 'preview.zoomIn': 'Увеличить',\n 'preview.zoomOut': 'Уменьшить',\n 'preview.zoomReset': 'Сбросить масштаб',\n 'preview.zoomLevel': '{percent}%',\n 'preview.copyPrompt': 'Копировать промпт',\n 'preview.copied': 'Скопировано',\n 'preview.addToEdit': 'В режим img2img',\n 'config.missing': 'API не настроен: откройте «Настройки → Плагины → Настраиваемые» и заполните api_url и api_key для ИИ-генерации.',\n 'config.generationTitle': 'Сначала настройте API генерации',\n 'config.generationHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Укажите адрес и ключ API генерации, сохраните — и можно творить.',\n 'config.enhancementTitle': 'Сначала настройте модель улучшения промптов',\n 'config.enhancementHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Укажите или переиспользуйте адрес и ключ диалогового API, выберите модель и сохраните.',\n 'config.disabledTitle': 'Плагин ИИ-генерации отключён',\n 'config.disabledHint': 'Открыты «Настройки → Плагины → ИИ-генерация». Включите плагин — и генерация заработает.',\n 'config.configured': 'Подключено: {url}',\n 'config.disabled': 'Плагин отключён — включите его в настройках.',\n 'connection.connected': 'Подключено',\n 'connection.disconnected': 'Нет подключения',\n 'panel.collapseConfig': 'Свернуть параметры',\n 'panel.expandConfig': 'Развернуть параметры',\n 'update.available': 'Доступна новая версия: {version}',\n 'update.install': 'Обновить онлайн',\n 'update.installing': 'Обновляем…',\n 'update.success': 'Обновлено до {version}, перезапустите DSH',\n 'update.failed': 'Не удалось обновить, попробуйте ещё раз',\n 'update.release': 'Открыть релиз',\n 'settings.title': 'ИИ-генерация (dsh-imagegen)',\n 'settings.description': 'Адрес и ключ API генерации изображений',\n 'settings.summary': 'Настройка каналов генерации изображений, моделей и переключателей плагина',\n 'settings.currentVersion': 'Текущая версия',\n 'settings.apiUrl': 'Адрес API (api_url)',\n 'settings.apiUrlHint': 'База OpenAI-совместимого API, например https://api.openai.com/v1; /images/generations и /images/edits дописываются автоматически',\n 'settings.apiKey': 'Ключ API (api_key)',\n 'settings.apiKeyHint': 'Bearer-ключ хранится открыто в локальном документе настроек; интерфейс показывает лишь факт его наличия',\n 'settings.apiKeySet': 'Ключ сохранён; введите новый, чтобы заменить, или нажмите «Очистить»',\n 'settings.apiKeyClear': 'Очистить',\n 'settings.imageModelsTitle': 'Модели генерации',\n 'settings.imageModelsHint': 'После сохранения адреса и ключа будут найдены кандидаты; выбирайте только те, что реально умеют генерировать изображения.',\n 'settings.imageModels': 'Разрешённые модели генерации',\n 'settings.imageModelsManualHint': 'По одной модели в строке; можно заполнить вручную, если API не отдаёт /models. Панель и агент используют только этот список.',\n 'settings.imageModelsFetch': 'Найти модели',\n 'settings.imageModelsLoading': 'Ищем…',\n 'settings.imageModelsCandidates': 'Найденные кандидаты (отметьте и сохраните)',\n 'settings.addModel': '+ Добавить вручную',\n 'settings.cancelAddModel': 'Скрыть добавление',\n 'settings.addModelPlaceholder': 'Имя модели, например qwen-image',\n 'settings.addModelConfirm': 'Добавить',\n 'settings.removeModel': 'Удалить модель',\n 'settings.optional': 'необязательно',\n 'settings.moreOptions': 'Дополнительно',\n 'settings.promptEnhanceTitle': 'Модель улучшения промптов',\n 'settings.promptEnhanceHint': 'Разворачивает краткое описание в полноценный промпт; пустые адрес и ключ наследуют настройки генерации.',\n 'settings.promptApiUrl': 'Адрес диалогового API (необязательно)',\n 'settings.promptApiUrlHint': 'OpenAI-совместимая база; если пусто — используется адрес API генерации.',\n 'settings.promptApiKey': 'Ключ диалогового API (необязательно)',\n 'settings.promptApiKeyHint': 'Если пусто — используется ключ API генерации.',\n 'settings.promptModel': 'Диалоговая модель',\n 'settings.promptModelHint': 'Выберите или впишите модель с поддержкой /chat/completions.',\n 'settings.promptModelDetectionHint': 'По умолчанию используется API генерации; после поиска просто выберите модель.',\n 'settings.promptModelsFetch': 'Получить модели',\n 'settings.promptModelsLoading': 'Получаем…',\n 'settings.promptModelsSelect': 'Выберите модель',\n 'settings.promptModelsCandidates': 'Найденные диалоговые модели',\n 'settings.addPromptModelPlaceholder': 'Имя диалоговой модели, например gpt-4.1-mini',\n 'settings.promptApiAdvanced': 'Отдельный диалоговый API (необязательно)',\n 'settings.announceToAgent': 'Оповещать агентов о плагине',\n 'settings.announceToAgentHint': 'Включено — возможности плагина добавляются в системный промпт каждого агента',\n 'settings.allowAgentImageGeneration': 'Разрешить агентам генерацию',\n 'settings.allowAgentImageGenerationHint': 'Включено по умолчанию. Выключено — агенты не могут создавать, смотреть или отменять задачи; боковая панель работает.',\n 'settings.enabled': 'Включить плагин',\n 'settings.enabledHint': 'Выключено — панель генерации недоступна (карточка настроек работает всегда)',\n 'settings.save': 'Сохранить',\n 'settings.saving': 'Сохраняем…',\n 'settings.discard': 'Отменить правки',\n 'settings.unsaved': 'Есть несохранённые правки',\n 'settings.saveFailed': 'Не удалось сохранить, попробуйте ещё раз',\n 'settings.readOnly': 'Документ настроек сейчас только для чтения, сохранить нельзя.',\n 'settings.notExposed': 'Пространство настроек недоступно: этот деплой не предоставляет сервис настроек плагина.',\n 'settings.expand': 'Развернуть',\n 'settings.collapse': 'Свернуть',\n 'settings.inherit': 'Наследовать',\n 'settings.on': 'Вкл',\n 'settings.off': 'Выкл',\n 'settings.overridden': 'Переопределено',\n 'settings.reset': 'Сбросить',\n 'settings.invalidNumber': 'Введите корректное число',\n\n\n\n 'templates.open': 'Шаблоны',\n 'templates.title': 'Библиотека промпт-шаблонов',\n 'templates.sources': 'Источники шаблонов',\n 'templates.meta': 'Всего шаблонов: {count} · {origin}',\n 'templates.origin.bundled': 'встроенный снимок',\n 'templates.origin.refreshed': 'обновлено онлайн',\n 'templates.search': 'Поиск по названиям и промптам…',\n 'templates.all': 'Все',\n 'templates.close': 'Закрыть',\n 'templates.back': 'К списку',\n 'templates.use': 'Использовать промпт',\n 'templates.copy': 'Копировать промпт',\n 'templates.copied': 'Скопировано',\n 'templates.refresh': 'Обновить библиотеку',\n 'templates.refreshing': 'Обновляем…',\n 'templates.refreshed': 'Обновлено, шаблонов: {count}',\n 'templates.refreshFailed': 'Ошибка обновления: {error}',\n 'templates.favorites': 'Избранное',\n 'templates.favoritesHint': 'Показать только избранные шаблоны',\n 'templates.favoritesEmpty': 'Пока пусто — нажмите звёздочку в углу карточки, чтобы сохранить шаблон',\n 'templates.favoriteAdd': 'Добавить в избранное',\n 'templates.favoriteRemove': 'Убрать из избранного',\n 'templates.favorite': 'В избранное',\n 'templates.unfavorite': 'В избранном',\n 'templates.cacheAll': 'Кэшировать все картинки',\n 'templates.cacheAllHint': 'Через локальный прокси сохранит все референсы этой библиотеки на диск — дальше доступно офлайн',\n 'templates.caching': 'Кэшируем {done}/{total}…',\n 'templates.cached': 'Все картинки закэшированы',\n 'templates.empty': 'Нет подходящих шаблонов',\n 'templates.loading': 'Загружаем библиотеку…',\n 'templates.loadFailed': 'Не удалось загрузить библиотеку: {error}',\n 'templates.retry': 'Повторить',\n 'templates.attribution': 'Шаблоны и изображения принадлежат сайтам-источникам; ссылки на авторов — в карточках',\n 'templates.source': 'Источник: {label}',\n 'templates.featured': 'Выбор редакции',\n 'inspiration.title': 'Вдохновение',\n 'inspiration.shuffle': 'Другие',\n 'inspiration.shuffling': 'Подбираем…',\n 'inspiration.useHint': 'Нажмите, чтобы использовать промпт',\n 'channels.title': 'Каналы',\n 'channels.hint': 'Укажите адрес и ключ API каждого канала — и модели станут доступны',\n 'channels.empty': 'Каналов пока нет — добавьте кнопкой ниже',\n 'channels.addProvider': 'Добавить провайдера',\n 'channels.addCustom': 'Добавить свой канал',\n 'channels.untitled': 'Канал без названия',\n 'channels.keySet': 'Ключ задан',\n 'channels.keyMissing': 'Ключ не задан',\n 'channels.modelCount': 'Моделей: {n}',\n 'channels.noModels': 'Модели не настроены',\n 'channels.defaultLabel': 'По умолчанию',\n 'channels.statusReady': 'Готов',\n 'channels.statusIncomplete': 'Настройка не завершена',\n 'channels.edit': 'Править',\n 'channels.delete': 'Удалить',\n 'channels.deleteConfirmTitle': 'Удалить канал «{name}»? Ключ канала будет удалён; история и галерея сохранятся.',\n 'channels.confirm': 'Удалить',\n 'channels.cancel': 'Отмена',\n 'channels.editorTitle': 'Канал',\n 'channels.editorSaveNote': 'Изменения вступят в силу после «Сохранить» внизу карточки',\n 'channels.displayName': 'Название',\n 'channels.apiUrl': 'Адрес API',\n 'channels.apiKey': 'Ключ API',\n 'channels.keyReplaceHint': 'Задан — введите новый, чтобы заменить',\n 'channels.keyMissingHint': 'Введите ключ',\n 'channels.keyClear': 'Очистить ключ',\n 'channels.modelCatalogTitle': 'Каталог моделей',\n 'channels.noModelsHint': 'Моделей пока нет: нажмите «Найти» для списка кандидатов или добавьте вручную; псевдоним по умолчанию равен id модели и его можно менять.',\n 'channels.detect': 'Найти снова',\n 'channels.detecting': 'Ищем…',\n 'channels.detectSuccess': 'Связь есть ✓ · кандидатов: {n}',\n 'channels.detectFailed': 'Ошибка поиска: {error}',\n 'channels.detectOk': 'API отвечает',\n 'channels.modelAliasLabel': 'Псевдоним (отображается)',\n 'channels.modelIdLabel': 'id модели у провайдера',\n 'channels.unknownProtocol': 'Протокол неизвестен — пробуем универсальный OpenAI',\n 'channels.removeModel': 'Удалить модель',\n 'channels.manualAddPlaceholder': 'id модели, например qwen-image',\n 'channels.addModelConfirm': 'Добавить',\n 'channels.copyFrom': 'Скопировать из канала…',\n 'channels.copyApply': 'Копировать',\n 'channels.candidatesTitle': 'Кандидаты (нажмите, чтобы добавить)',\n 'channels.deleteThisChannel': 'Удалить этот канал',\n 'channels.setDefault': 'Сделать каналом по умолчанию',\n 'channels.presetPickerTitle': 'Добавить провайдера',\n 'channels.presetPickerHint': 'Выберите встроенного провайдера — останется ввести только ключ API',\n 'channels.presetCustomHint': 'Свой адрес, ключ и каталог моделей',\n 'channels.presetLoadFailed': 'Не удалось загрузить провайдеров: {error}',\n 'canvas.skills.fileNode': 'Узел файла',\n 'canvas.skills.addFileNode': 'Добавить узел файла',\n 'canvas.skills.emptyFileNode': 'Нажмите, чтобы загрузить файл',\n 'canvas.skills.fileMenuUpload': 'Загрузить файл',\n 'canvas.skills.fileDropHint': 'Перетащите файл сюда для загрузки',\n 'canvas.skills.fileDropSub': 'PDF / Office / текст / изображения, до 50 МБ',\n 'canvas.skills.fileSizeUnknown': 'Размер неизвестен',\n 'canvas.skills.filePreviewUnavailable': 'Для этого формата нет предпросмотра — скачайте файл или запустите навык',\n 'canvas.skills.filePreviewLoading': 'Загружаем предпросмотр…',\n 'canvas.skills.fileDownload': 'Скачать файл',\n 'canvas.skills.fileOpen': 'Открыть системным приложением',\n 'canvas.skills.fileKind.text': 'Текст',\n 'canvas.skills.fileKind.pdf': 'PDF',\n 'canvas.skills.fileKind.image': 'Изображение',\n 'canvas.skills.fileKind.office': 'Документ',\n 'canvas.skills.fileKind.archive': 'Архив',\n 'canvas.skills.fileKind.audio': 'Аудио',\n 'canvas.skills.fileKind.video': 'Видео',\n 'canvas.skills.fileKind.other': 'Файл',\n 'canvas.skills.fileTooLarge': 'Файл превышает лимит {size} МБ',\n 'canvas.skills.fileTypeBlocked': 'Файлы {ext} не загружаются из соображений безопасности',\n 'canvas.skills.fileUploadFailed': 'Не удалось загрузить: {message}',\n 'canvas.preview.expand': 'Открыть предпросмотр',\n 'canvas.preview.close': 'Закрыть предпросмотр',\n 'canvas.preview.copy': 'Скопировать содержимое',\n 'canvas.preview.copied': 'Скопировано',\n 'canvas.preview.openTab': 'Открыть в новой вкладке',\n 'canvas.preview.retry': 'Повторить',\n 'canvas.preview.failed': 'Не удалось прочитать предпросмотр: {message}',\n 'canvas.preview.empty': 'Файл пуст',\n 'canvas.preview.textTruncated': 'Большой файл — показаны первые {chars} символов',\n 'canvas.preview.tableTruncated': 'Большая таблица — показаны первые {rows} строк',\n 'canvas.preview.archiveTruncated': 'Много записей — показаны первые {count}',\n 'canvas.preview.docTruncated': 'Длинный документ — показаны первые {blocks} блоков',\n 'canvas.preview.slidesTruncated': 'Большая презентация — показаны первые {count} слайдов',\n 'canvas.preview.slideOf': 'Слайд {page}',\n 'canvas.preview.rendered': 'Рендер',\n 'canvas.preview.source': 'Исходник',\n 'canvas.preview.pdfFallback': 'В этой среде PDF не отображается встроенно — скачайте файл',\n 'canvas.preview.pdfHeuristic': 'Текст PDF извлекается эвристически; сложная вёрстка может потерять порядок',\n 'canvas.preview.reason.unsupported': 'В этом формате нет читаемого содержимого — скачайте файл или превратите его в узел изображения или текста для навыка',\n 'canvas.preview.reason.unreadable': 'Из файла не удалось извлечь текст (скан или необычная кодировка); преобразуйте его в узел изображения для визуальной модели',\n 'canvas.preview.doubleClick': 'Двойной щелчок открывает предпросмотр',\n 'canvas.skills.button': 'Навык',\n 'canvas.skills.menuTitle': 'Запустить навык',\n 'canvas.skills.runOn': 'Запустить навык на «{name}»',\n 'canvas.skills.pickerTitle': 'Выберите навык',\n 'canvas.skills.searchPlaceholder': 'Поиск навыков…',\n 'canvas.skills.groupBuiltin': 'Встроенные действия',\n 'canvas.skills.groupExternal': 'Локальные навыки',\n 'canvas.skills.emptyExternal': 'Локальных навыков нет — установите их в ~/.dsh/skills и обновите',\n 'canvas.skills.empty': 'Навыки недоступны',\n 'canvas.skills.loading': 'Загружаем список навыков…',\n 'canvas.skills.tierLight': 'Быстро',\n 'canvas.skills.tierHeavy': 'Долгая задача',\n 'canvas.skills.accepts': 'Работает с: {types}',\n 'canvas.skills.incompatible': 'Навык не применим к этому типу узла',\n 'canvas.skills.disabled': 'Навыки холста отключены в настройках плагина',\n 'canvas.skills.noRegistry': 'В этом хосте нет реестра навыков (ctx.skills) — доступны только встроенные действия',\n 'canvas.skills.registryFailed': 'Не удалось получить список навыков: {message}',\n 'canvas.skills.needAgent': 'Нужен агентный рантайм хоста, которого нет в этой сборке DSH — долгие задачи недоступны',\n 'canvas.skills.unknown': 'Такого навыка больше нет',\n 'canvas.skills.canvasMissing': 'Холст не найден — обновите страницу и повторите',\n 'canvas.skills.noInput': 'У этого узла нет данных для обработки',\n 'canvas.skills.noChat': 'Быстрым навыкам нужна чат-модель: Настройки → Плагины → ИИ-генерация → Улучшение промпта',\n 'canvas.skills.noDescription': '(описание не указано)',\n 'canvas.skills.externalName': '{name}',\n 'canvas.skills.externalSystem': 'Вы выполняете навык «{name}». Строго следуйте его инструкциям для входа ниже, отвечайте на языке входа и выводите только результат — не пересказывайте инструкции.',\n 'canvas.skills.externalHeader': 'Примените навык «{name}» к данным ниже.',\n 'canvas.skills.externalHeavyCost': 'Это долгая задача: запускается безголовый агент и может израсходовать много квоты модели.',\n 'canvas.skills.extraInstruction': 'Дополнительное требование:',\n 'canvas.skills.inputsHeader': 'Входные данные:',\n 'canvas.skills.inputsEmpty': '(читаемого текста нет — работайте по приложенным изображениям)',\n 'canvas.skills.needInstruction': 'Введите своё указание для обработки',\n 'canvas.skills.needImage': 'Этому навыку нужен узел изображения',\n 'canvas.skills.needText': 'Этому навыку нужен текстовый узел с содержимым',\n 'canvas.skills.fileMissing': 'Файловый ресурс отсутствует или удалён',\n 'canvas.skills.extractFailed': 'Из этого файла не удалось извлечь содержимое',\n 'canvas.skills.nothingToRead': 'Читаемого содержимого не найдено',\n 'canvas.skills.truncated': 'Содержимое обрезано до {count} символов',\n 'canvas.skills.tooManyImages': 'Изображений больше {count} — обработаны только первые',\n 'canvas.skills.extractNodeTitle': 'Содержимое «{name}»',\n 'canvas.skills.summaryHead': 'Собрано узлов-источников: {count}',\n 'canvas.skills.describeAsk': 'Опишите эти изображения ({count}).',\n 'canvas.skills.polishNodeTitle': 'Результат обработки ({style})',\n 'canvas.skills.skillMissing': 'Навык «{name}» не установлен. Сначала установите его (например, попросите агента DSH установить https://github.com/ningzimu/image-to-editable-ppt-skill) и повторите.',\n 'canvas.skills.agentFailed': 'Не удалось создать безголового агента — проверьте модель и пресет агента',\n 'canvas.skills.needModel': 'Хост не публикует модель по умолчанию (agent-default-model), поэтому долгая задача не может запустить агента. Выберите модель по умолчанию в «Настройки → Общие» и повторите.',\n 'canvas.skills.noOutputFile': 'Навык не создал файл; ответ агента сохранён текстовым узлом',\n 'canvas.skills.heavyIntro': 'Вы выполняете навык «{name}» для визуального холста. Пройдите всю процедуру строго по документации, не пропуская шагов.',\n 'canvas.skills.heavyBodyHeader': 'Инструкции навыка:',\n 'canvas.skills.heavyInputsHeader': 'Входные файлы задачи (абсолютные пути):',\n 'canvas.skills.heavyOutputHeader': 'Запишите итоговый артефакт в этот каталог: {dir}',\n 'canvas.skills.heavyRules': 'Требования:\\n1. Все промежуточные файлы держите в рабочем каталоге; итоговый артефакт — только в каталоге выше.\\n2. По завершении коротко ответьте: имена файлов, успех, замечания.',\n 'canvas.skills.heavyAsk': 'Выполните навык «{name}».',\n 'canvas.skills.heavyAskInputs': 'Каталог входных файлов: {dir}',\n 'canvas.skills.heavyAskOutput': 'Каталог вывода: {dir}',\n 'canvas.skills.heavyAskLoadSkill': '(Если инструкций навыка нет в контексте, сначала загрузите их по имени «{name}».)',\n 'canvas.skills.stageQueued': 'В очереди',\n 'canvas.skills.stagePreparing': 'Готовим входные файлы',\n 'canvas.skills.stageReading': 'Читаем содержимое узлов',\n 'canvas.skills.stageRunning': 'Навык выполняется',\n 'canvas.skills.stageCollecting': 'Собираем артефакты',\n 'canvas.skills.stageDone': 'Готово',\n 'canvas.skills.stageCancelled': 'Отменено',\n 'canvas.skills.stagePrepared': 'Подготовлено входных файлов: {count}',\n 'canvas.skills.stageArtifacts': 'Выполняется — создано файлов: {count}',\n 'canvas.skills.runElapsed': 'Прошло',\n 'canvas.skills.runRetry': 'Повторить',\n 'canvas.skills.runStarted': 'Навык «{name}» запущен',\n 'canvas.skills.runDone': 'Навык завершён — новых узлов: {count}',\n 'canvas.skills.runFailed': 'Навык завершился ошибкой: {message}',\n 'canvas.skills.runCancelled': 'Запуск навыка отменён',\n 'canvas.skills.cancel': 'Отменить запуск',\n 'canvas.skills.cancelling': 'Отменяем…',\n 'canvas.skills.confirmTitle': 'Запустить долгую задачу',\n 'canvas.skills.confirmBody': '«{name}» запускает безголового агента на этой машине. Это может занять от нескольких до десятков минут и израсходовать много квоты модели.',\n 'canvas.skills.confirmCost': 'Стоимость: {hint}',\n 'canvas.skills.confirmRequires': 'Требуется: {list}',\n 'canvas.skills.confirmRun': 'Запустить',\n 'canvas.skills.confirmCancel': 'Не сейчас',\n 'canvas.skills.batchTitle': 'Пакетный запуск',\n 'canvas.skills.batchNote': 'Запуск по одному узлу подряд (узлов: {count})',\n 'canvas.skills.batchTooMany': 'Не более {count} узлов за запуск',\n 'canvas.skills.sourceBadge': 'Результат навыка',\n 'canvas.skills.sourceTip': 'Создано навыком «{name}» из узлов-источников: {count}',\n 'canvas.skills.rerun': 'Запустить тот же навык снова',\n 'canvas.skills.history': 'История запусков',\n 'canvas.skills.historyEmpty': 'Запусков пока нет',\n 'canvas.skills.needConfirm': 'Этот навык требует подтверждения',\n 'canvas.skills.polish': 'Обработка текста',\n 'canvas.skills.polishDesc': 'Переписать текстовый узел в выбранном стиле: формальнее / разговорнее / короче / подробнее или своё указание',\n 'canvas.skills.describe': 'Описание изображения',\n 'canvas.skills.describeDesc': 'Прочитать изображение визуальной моделью и создать текстовый узел (годится как промпт)',\n 'canvas.skills.extract': 'Извлечь содержимое',\n 'canvas.skills.extractDesc': 'Извлечь текст из файла или изображения в текстовый узел (текст / код / OOXML / PDF)',\n 'canvas.skills.ppt': 'Изображение → редактируемый PPT',\n 'canvas.skills.pptDesc': 'Запускает локальный навык image-to-editable-ppt и создаёт редактируемый .pptx как узел файла',\n 'canvas.skills.pptCost': 'Долгая задача: презентация на 10 страниц может израсходовать много квоты; одна страница часто занимает 10+ минут',\n 'canvas.skills.pptRequires': 'Нужен установленный навык image-to-editable-ppt, а также токен OCR и образный бэкенд по его документации',\n 'canvas.polish.button': 'ИИ-обработка',\n 'canvas.polish.title': 'ИИ-обработка текста',\n 'canvas.polish.formal': 'Формальнее',\n 'canvas.polish.casual': 'Разговорнее',\n 'canvas.polish.shorter': 'Короче',\n 'canvas.polish.expand': 'Подробнее',\n 'canvas.polish.custom': 'Своё',\n 'canvas.polish.customPlaceholder': 'напр. переписать как пост для соцсети с эмодзи и хэштегами',\n 'canvas.polish.run': 'Обработать',\n 'canvas.polish.previewTitle': 'Было / стало',\n 'canvas.polish.apply': 'Применить',\n 'canvas.polish.revert': 'Вернуть',\n 'canvas.polish.applied': 'Результат применён',\n 'canvas.polish.directHint': 'Текущая настройка: результат сразу заменяет текст (в настройках можно включить предпросмотр)',\n 'settings.skillsEnabled': 'Навыки холста',\n 'canvas.skills.libraryButton': 'Библиотека навыков',\n 'canvas.skills.libraryTitle': 'Библиотека навыков',\n 'canvas.skills.libraryInstalled': 'Установленные навыки ({count})',\n 'canvas.skills.libraryEmpty': 'Навыков пока нет. Установите по ссылке на репозиторий или загрузите архив.',\n 'canvas.skills.libraryRoot': 'Каталог установки: {root}',\n 'canvas.skills.libraryInstall': 'Установка по ссылке',\n 'canvas.skills.libraryInstallHint': 'Репозиторий или папка GitHub, отдельный SKILL.md, любой git-адрес или ссылка на zip; несколько ссылок — с новой строки. Хост подхватывает установку без перезапуска.',\n 'canvas.skills.libraryUrlPlaceholder': 'https://github.com/owner/repo или …/blob/main/SKILL.md',\n 'canvas.skills.libraryUpload': 'Загрузить архив',\n 'canvas.skills.libraryUploadHint': 'Загрузите zip с файлом SKILL.md (≤50 МБ): он распакуется в ~/.dsh/skills.',\n 'canvas.skills.libraryDropzone': 'Нажмите или перетащите zip-архив сюда',\n 'canvas.skills.libraryForce': 'Заменить навык с тем же именем',\n 'canvas.skills.libraryInstallAction': 'Установить',\n 'canvas.skills.libraryInstalling': 'Установка…',\n 'canvas.skills.libraryRemove': 'Удалить',\n 'canvas.skills.libraryRemoving': 'Удаление…',\n 'canvas.skills.libraryRemoveConfirm': 'Удалить навык «{name}»? Его папка будет стёрта.',\n 'canvas.skills.libraryInstalledToast': 'Установлено: {names}',\n 'canvas.skills.libraryFailedToast': 'Ошибка установки: {message}',\n 'canvas.skills.libraryRemovedToast': 'Удалено: {name}',\n 'canvas.skills.libraryUnavailable': 'Этот хост не управляет библиотекой навыков',\n 'canvas.skills.libraryReload': 'Обновить',\n 'canvas.skills.libraryOpenFolder': 'Открыть каталог',\n 'canvas.skills.installNow': 'Установить',\n 'canvas.skills.needsInstall': 'Не установлен',\n 'canvas.skills.missingHint': 'Навык «{name}» ещё не установлен — установите его, чтобы использовать',\n 'canvas.skills.installArchiveFailed': 'Ошибка установки архива: {message}',\n 'canvas.skills.libraryIssueFrontmatter': 'Нет YAML frontmatter (нужны name и description), поэтому реестр навыков хоста его не загружает и узлы не могут его запустить',\n 'canvas.skills.libraryIssueName': 'Имя в frontmatter «{name}» не в kebab-case (строчные буквы, цифры и дефисы), поэтому реестр навыков хоста его игнорирует и узлы не могут его запустить',\n 'canvas.skills.libraryIssueDescription': 'В frontmatter файла SKILL.md нет description (обязательно), поэтому реестр навыков хоста его не загружает и узлы не могут его запустить',\n 'canvas.skills.libraryIssueInvocation': 'Запись disable-model-invocation / user-invocable не разбирается, поэтому реестр навыков хоста его игнорирует и узлы не могут его запустить',\n 'canvas.skills.libraryIssueUserOnly': 'Навык объявляет disable-model-invocation: он доступен только человеку, поэтому узлы холста не могут его запустить',\n 'canvas.skills.configOpen': 'Настроить',\n 'canvas.skills.configSave': 'Сохранить',\n 'canvas.skills.configSaveApply': 'Сохранить и применить',\n 'canvas.skills.configSaving': 'Сохранение…',\n 'canvas.skills.configApplying': 'Применение…',\n 'canvas.skills.configSaved': 'Сохранено',\n 'canvas.skills.configSaveFailed': 'Не удалось сохранить',\n 'canvas.skills.configApplied': 'Сохранено и применено',\n 'canvas.skills.configApplyFailed': 'Не удалось применить',\n 'canvas.skills.configClear': 'Очистить',\n 'canvas.skills.configSecretSet': 'Задан (пустое поле оставит как есть)',\n 'canvas.skills.configSecretUnset': 'Не задан',\n 'canvas.skills.configMissingHint': 'Требуется настройка: {fields}',\n 'canvas.skills.configGo': 'Настроить',\n 'canvas.skills.configSourceSkill': 'Поля объявлены собственным skill.config.json навыка',\n 'canvas.skills.configSourcePlugin': 'Поля взяты из встроенного рецепта плагина для известного навыка (собственное объявление навыка важнее)',\n 'canvas.skills.configHeader': 'Уже настроено:',\n 'canvas.skills.configUnwritable': 'Этот хост не может записывать настройки, поэтому настройка навыков недоступна',\n 'canvas.skills.configIssueVersion': 'В skill.config.json указана версия, отличная от 1, поэтому объявление проигнорировано',\n 'canvas.skills.configIssueUnreadable': 'skill.config.json не удалось разобрать (ошибка JSON), поэтому объявление проигнорировано',\n 'canvas.skills.configIssueEmpty': 'В skill.config.json нет пригодных полей или шагов, поэтому он проигнорирован',\n}\n","/**\n * Locale dictionaries and the pure interpolator.\n *\n * This module is the React-free half of the plugin's copy layer: the host half\n * imports the very same dictionaries to render server-produced text (skill\n * catalog labels, error messages, produced node titles) in the language the\n * user picked in the DSH interface. The browser half layers the live locale\n * binding (and its change subscription) on top in `helpers.ts`.\n *\n * Both halves must agree on key names, so the tables live here — not in a file\n * that also holds React state, which the two bundles would instantiate twice.\n */\n\nimport { en, ru, zh, type ImageGenKey } from './client/locales.ts'\n\n/** Languages with a shipped dictionary. */\nexport type ImageGenLanguage = 'zh' | 'en' | 'ru'\n\n/** Template values accepted by the interpolator. */\nexport type TranslateValues = Record<string, string | number>\n\n/** Every shipped dictionary, keyed by language. */\nexport const IMAGEGEN_DICTIONARIES: Record<ImageGenLanguage, Record<string, string>> = { zh, en, ru }\n\n/** Normalize any DSH locale id onto a shipped dictionary (unknown → English). */\nexport function imageGenLanguageOf(value: unknown): ImageGenLanguage {\n const head = typeof value === 'string' ? value.trim().toLowerCase().split(/[-_]/)[0] ?? '' : ''\n return head === 'zh' || head === 'ru' ? head : 'en'\n}\n\n/**\n * Translate one key with optional `{name}` template params, falling back to\n * Chinese (the key source) and finally to the key itself.\n * @param key - dictionary key.\n * @param values - optional `{name}` substitutions.\n * @param language - shipped language to resolve against.\n * @returns the rendered copy.\n */\nexport function interpolate(key: string, values: TranslateValues | undefined, language: ImageGenLanguage): string {\n const text = IMAGEGEN_DICTIONARIES[language][key] ?? zh[key as ImageGenKey] ?? key\n if (values === undefined) return text\n let rendered: string = text\n for (const [name, value] of Object.entries(values)) {\n rendered = rendered.replaceAll(`{${name}}`, String(value))\n }\n return rendered\n}\n","/**\n * Shared panel helpers: the active-dictionary pick bound to the dsh-imagegen\n * interpolator, the plugin locale that follows the DSH interface language\n * (bridged in client/index.ts from ctx.locale — the plugin ships zh / en / ru\n * and registers itself as a DSH language pack for Русский), plus a small\n * error-message extractor. All copy stays in the locale dictionaries.\n *\n * The dictionaries themselves live in ../locale-tables.ts so the host half can\n * render the same copy without duplicating the tables.\n */\n\nimport { imageGenLanguageOf, interpolate, type ImageGenLanguage, type TranslateValues } from '../locale-tables.ts'\nimport type { ImageGenKey } from './locales.ts'\n\nexport type { ImageGenLanguage, TranslateValues }\n\n/** The active DSH locale mapped onto our dictionary (module-level, one value per app). */\nlet activeLocale: ImageGenLanguage = 'zh'\n\n/** Bumped on every locale change; useSyncExternalStore version. */\nlet languageVersion = 0\n\nconst languageListeners = new Set<() => void>()\n\n/**\n * Adopt the DSH interface language. Unknown ids (future language packs)\n * resolve to English — the same per-key fallback convention the host locale\n * chain uses.\n */\nexport function applyHostLocale(id: unknown): void {\n const next = imageGenLanguageOf(id)\n if (next === activeLocale) return\n activeLocale = next\n languageVersion += 1\n for (const listener of [...languageListeners]) listener()\n}\n\n/** Monotonic version of the active locale (external-store snapshot). */\nexport function getImageGenLanguageVersion(): number {\n return languageVersion\n}\n\n/** Observe locale changes; returns the unsubscriber. */\nexport function subscribeImageGenLanguage(listener: () => void): () => void {\n languageListeners.add(listener)\n return () => { languageListeners.delete(listener) }\n}\n\n/** The active shipped language (also used to translate host-sent copy). */\nexport function activeImageGenLanguage(): ImageGenLanguage {\n return activeLocale\n}\n\n/** Translate a key with optional {name} template params (current language). */\nexport function tt(key: ImageGenKey, values?: TranslateValues): string {\n return interpolate(key, values, activeLocale)\n}\n\n/** Human-readable error text from an unknown thrown value. */\nexport function errorMessage(error: unknown): string {\n if (error instanceof Error) return error.message\n return String(error)\n}\n","/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Self-contained slice of the same\n * pattern the dsh-web-ui family cards use (this package must not depend on a\n * sibling UI package).\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope, SettingsScopeSnapshot } from './settings-scope.ts'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n /**\n * True for secret fields (role('secret') in the namespace schema): the\n * redacted wire view never returns the stored value, so the form treats an\n * empty draft as \"no change\" and judges writes by the namespace's secrets\n * sidecar instead of the user layer.\n */\n secret?: boolean\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /** Whether the namespace is actually served (the bridge answered). */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** A newline/comma-separated model list, persisted as a normalized string array. */\nexport function stringListField(field: string): FieldSpec {\n return {\n field,\n format: value => Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string').join('\\n') : '',\n parse: (text) => {\n const values = [...new Set(text.split(/[\\n,]/).map(item => item.trim()).filter(Boolean))]\n return values.length === 0 ? { kind: 'clear' } : { kind: 'set', value: values }\n },\n }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n if (text === 'true') return { kind: 'set', value: true }\n if (text === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/**\n * A secret field (role('secret') in the namespace schema). The stored value is\n * never rendered or returned by the redacted wire view, so:\n * - an empty draft means \"no change\" (typing nothing must never clear an\n * invisible stored key); the dedicated clear action stages an explicit clear;\n * - a write's outcome is judged by the namespace's secrets sidecar through\n * the {@link CardForm} `secretSettled` hook, never by the user layer.\n */\nexport function secretField(field: string): FieldSpec {\n return {\n field,\n secret: true,\n format: () => '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return undefined\n return { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings scope and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n /**\n * @param scope - the bound settings scope for this card's namespace.\n * @param specs - the fields this card edits.\n * @param options.secretSettled - for secret fields, whether the namespace\n * currently holds a stored secret (the redacted view never round-trips the\n * value, so a write's outcome is read from the secrets sidecar instead).\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n private readonly options: { secretSettled?: (field: string) => boolean } = {},\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n scope.subscribe(() => { this.publish() })\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n // A secret field's empty draft is \"no change\" (see secretField), never\n // an invalid state — the stored value is invisible, so the user cannot\n // be expected to type over it.\n invalid: write === undefined && !(spec.secret === true && staged.text.trim() === ''),\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n */\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field)\n if (staged.clear) {\n const present = spec.secret === true\n ? (this.options.secretSettled?.(field) ?? false)\n : this.stored(field)\n if (present) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n const spec = this.specOf(field)\n if (spec.secret === true) return !(this.options.secretSettled?.(field) ?? false)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n const spec = this.specOf(field)\n if (spec.secret === true) return this.options.secretSettled?.(field) ?? true\n return this.userLayer()?.[field] === value\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n","/**\n * Staged form model for the channel list of the settings card. Mirrors the\n * CardForm staging pattern (dirty → one save) but for a structured value, so\n * the card can edit N channels, per-channel keys, and the default-channel\n * flag, then persist everything in one revision-fenced mutate call.\n *\n * Storage rules (dictated by dsh-settings semantics):\n * - the whole `channels` array is written wholesale via `path: ['channels']`;\n * - every channel's API key is written to `path: ['channelSecrets', <id>]`,\n * one key at a time so untouched keys are never clobbered by a save the\n * reader could not see (keys are redacted out of the wire view). The host\n * bridge diverts that path into the credential store, so the value never\n * reaches the settings document — this module only speaks the wire shape;\n * - path ops never navigate *inside* the channels array.\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { ChannelConfig, ChannelProtocolPreference, ModelMapping, SubscriptionProvider } from '../protocol.ts'\nimport type { ImageGenScope, SettingsOp } from './settings-scope.ts'\n\n/** One channel as the editor stages it (secrets never travel here). */\nexport interface ChannelDraft {\n id: string\n preset: string\n name: string\n apiUrl: string\n apiUrlFull: boolean\n protocol?: ChannelProtocolPreference\n auth?: 'api-key' | 'subscription'\n subscription?: SubscriptionProvider\n models: ModelMapping[]\n}\n\n/** One staged key edit for a channel. */\nexport type KeyEdit = { kind: 'set'; value: string } | { kind: 'clear' }\n\n/** The state the card renders. */\nexport interface ChannelsFormState {\n /** Staged channel list (the scope value when nothing is staged). */\n channels: ChannelDraft[]\n /** Which channels currently hold a stored secret (staged edits included). */\n keySet: Record<string, boolean>\n /** The effective default channel id. */\n defaultChannelId: string\n /** The effective default image-model alias (first model when unset). */\n defaultModel: string\n /** Whether a save would write anything. */\n dirty: boolean\n /** Whether the document accepts writes. */\n writable: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save failed (cleared by the next edit or save). */\n failed: boolean\n}\n\n/** The actions the card's slot entry injects. */\nexport interface ChannelsFormActions {\n /** Replace the whole channel list (add/edit/remove go through here). */\n setChannels: (channels: ChannelDraft[]) => void\n /** Stage a key for one channel ('' clears; undefined = no staged change). */\n setChannelKey: (id: string, value: string | undefined) => void\n /** Stage the default-channel flag. */\n setDefaultChannel: (id: string) => void\n /** Stage the default image-model alias. */\n setDefaultModel: (model: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n commit: () => Promise<void>\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** Deep equality over JSON-compatible data (the change predicate). */\nfunction deepEqualJson(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false\n return a.every((entry, index) => deepEqualJson(entry, b[index]))\n }\n const left = a as Record<string, unknown>\n const right = b as Record<string, unknown>\n const keys = Object.keys(left)\n if (keys.length !== Object.keys(right).length) return false\n return keys.every(key => key in right && deepEqualJson(left[key], right[key]))\n}\n\n/** Trim and normalize a draft channel (models never carry empty aliases). */\nfunction stripChannel(channel: ChannelDraft): ChannelDraft {\n const models = channel.models\n .map(model => ({ alias: model.alias.trim(), id: model.id.trim() === '' ? model.alias.trim() : model.id.trim() }))\n .filter(model => model.alias !== '')\n return {\n id: channel.id,\n preset: channel.preset,\n name: channel.name.trim(),\n apiUrl: channel.apiUrl.trim(),\n apiUrlFull: channel.apiUrlFull === true,\n ...channel.protocol === undefined ? {} : { protocol: channel.protocol },\n ...channel.auth === undefined ? {} : { auth: channel.auth },\n ...channel.subscription === undefined ? {} : { subscription: channel.subscription },\n models: [...new Map(models.map(model => [model.alias, model])).values()],\n }\n}\n\nexport class ChannelsForm {\n private stagedChannels: ChannelDraft[] | null = null\n private readonly stagedKeys = new Map<string, KeyEdit>()\n private stagedDefault: string | null = null\n private stagedDefaultModel: string | null = null\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n constructor(private readonly scope: ImageGenScope) {\n scope.subscribe(() => { this.publish() })\n scope.subscribeSecretSets(() => { this.publish() })\n }\n\n /** Publish a projection of this form, rebuilt on every scope or draft change. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Subscribe to staged and persisted channel changes. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /** The staged channel list, or the scope value when nothing is staged. */\n private channelsValue(): ChannelDraft[] {\n const view = this.scope.getSnapshot().value as { channels?: ChannelConfig[] } | undefined\n return this.stagedChannels ?? (Array.isArray(view?.channels) ? view.channels.map(toDraft) : [])\n }\n\n /** Whether a channel currently holds a stored or staged secret. */\n private keyHeld(id: string): boolean {\n const edit = this.stagedKeys.get(id)\n if (edit !== undefined) return edit.kind === 'set' && edit.value !== ''\n return this.scope.getSecretSetSnapshot(`channelSecrets.${id}`)\n }\n\n private defaultValue(): string {\n if (this.stagedDefault !== null) return this.stagedDefault\n const view = this.scope.getSnapshot().value as { defaultChannelId?: string } | undefined\n const channels = this.channelsValue()\n if (view?.defaultChannelId !== undefined && channels.some(channel => channel.id === view.defaultChannelId)) return view.defaultChannelId\n return channels[0]?.id ?? ''\n }\n\n private modelAliases(): string[] {\n return [...new Set(this.channelsValue().flatMap(channel => channel.models.map(model => model.alias)).filter(alias => alias !== ''))]\n }\n\n private defaultModelValue(): string {\n if (this.stagedDefaultModel !== null) return this.stagedDefaultModel\n const models = this.modelAliases()\n const view = this.scope.getSnapshot().value as { defaultModel?: string } | undefined\n if (typeof view?.defaultModel === 'string' && models.includes(view.defaultModel)) return view.defaultModel\n return models[0] ?? ''\n }\n\n private dirtyValue(): boolean {\n const channels = this.channelsValue()\n const stagedChanged = this.stagedChannels !== null && !deepEqualJson(this.stagedChannels, scopeChannelsOf(this.scope))\n const scopeView = this.scope.getSnapshot().value as { defaultChannelId?: string; defaultModel?: string } | undefined\n const scopeDefault = scopeView?.defaultChannelId ?? channels[0]?.id ?? ''\n const defaultChanged = this.stagedDefault !== null && this.stagedDefault !== scopeDefault\n const models = this.modelAliases()\n const storedModel = typeof scopeView?.defaultModel === 'string' && models.includes(scopeView.defaultModel)\n ? scopeView.defaultModel\n : models[0] ?? ''\n const modelChanged = this.stagedDefaultModel !== null && this.stagedDefaultModel !== storedModel\n return stagedChanged || defaultChanged || modelChanged || this.stagedKeys.size > 0\n }\n\n /** The card-facing snapshot. */\n snapshot(): ChannelsFormState {\n const channels = this.channelsValue()\n const keySet: Record<string, boolean> = {}\n for (const channel of channels) keySet[channel.id] = this.keyHeld(channel.id)\n return {\n channels,\n keySet,\n defaultChannelId: this.defaultValue(),\n defaultModel: this.defaultModelValue(),\n dirty: this.dirtyValue(),\n writable: this.scope.getSnapshot().writable !== false,\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): ChannelsFormActions {\n return {\n setChannels: (channels) => { this.stageChannels(channels) },\n setChannelKey: (id, value) => { this.stageKey(id, value) },\n setDefaultChannel: (id) => { this.stagedDefault = id; this.failed = false; this.publish() },\n setDefaultModel: (model) => { this.stagedDefaultModel = model; this.failed = false; this.publish() },\n commit: () => this.commit(),\n discard: () => {\n if (this.stagedChannels === null && this.stagedKeys.size === 0 && this.stagedDefault === null && this.stagedDefaultModel === null && !this.failed) return\n this.stagedChannels = null\n this.stagedKeys.clear()\n this.stagedDefault = null\n this.stagedDefaultModel = null\n this.failed = false\n this.publish()\n },\n }\n }\n\n // ------------------------------------------------------------------ staging\n\n private stageChannels(channels: ChannelDraft[]): void {\n const cleaned = channels.map(stripChannel)\n this.stagedChannels = cleaned\n this.failed = false\n this.publish()\n }\n\n private stageKey(id: string, value: string | undefined): void {\n if (value === undefined || value.trim() === '') {\n if (this.keyHeld(id)) this.stagedKeys.set(id, { kind: 'clear' })\n // An empty key for a key-less channel stages nothing.\n } else {\n this.stagedKeys.set(id, { kind: 'set', value: value.trim() })\n }\n this.failed = false\n this.publish()\n }\n\n // ----------------------------------------------------------------- save\n\n /** Build the single batch of path ops a save performs. */\n private planOps(): SettingsOp[] {\n const ops: SettingsOp[] = []\n if (this.stagedChannels !== null) {\n ops.push({ op: 'set', path: ['channels'], value: this.stagedChannels })\n // Once channels exist, the legacy flat fields are obsolete (idempotent).\n ops.push({ op: 'unset', path: ['apiUrl'] })\n ops.push({ op: 'unset', path: ['apiKey'] })\n ops.push({ op: 'unset', path: ['imageModels'] })\n }\n for (const [id, edit] of this.stagedKeys) {\n if (edit.kind === 'set') ops.push({ op: 'set', path: ['channelSecrets', id], value: edit.value })\n else ops.push({ op: 'unset', path: ['channelSecrets', id] })\n }\n if (this.stagedDefault !== null) {\n ops.push({ op: 'set', path: ['defaultChannelId'], value: this.stagedDefault })\n }\n if (this.stagedDefaultModel !== null) {\n ops.push(this.stagedDefaultModel === ''\n ? { op: 'unset', path: ['defaultModel'] }\n : { op: 'set', path: ['defaultModel'], value: this.stagedDefaultModel })\n }\n return ops\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after the write settles.\n */\n async commit(): Promise<void> {\n if (this.saving) return\n const ops = this.planOps()\n if (ops.length === 0) return\n this.saving = true\n this.failed = false\n this.publish()\n try {\n await this.scope.mutateOps(ops)\n this.stagedChannels = null\n this.stagedKeys.clear()\n this.stagedDefault = null\n this.stagedDefaultModel = null\n this.failed = false\n } catch {\n this.failed = true\n } finally {\n this.saving = false\n this.publish()\n }\n }\n\n private publish(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n\n/** Project a stored channel into a draft (secrets never travel in channels). */\nfunction toDraft(channel: ChannelConfig): ChannelDraft {\n return { id: channel.id, preset: channel.preset, name: channel.name, apiUrl: channel.apiUrl, apiUrlFull: channel.apiUrlFull === true, ...channel.protocol === undefined ? {} : { protocol: channel.protocol }, ...channel.auth === undefined ? {} : { auth: channel.auth }, ...channel.subscription === undefined ? {} : { subscription: channel.subscription }, models: channel.models.map(model => ({ ...model })) }\n}\n\n/** The scope's current channels value (a plain array), for change detection. */\nfunction scopeChannelsOf(scope: ImageGenScope): unknown {\n const view = scope.getSnapshot().value as { channels?: unknown } | undefined\n return Array.isArray(view?.channels) ? view.channels : []\n}\n","/**\n * Model catalog: protocol-family detection and capability annotation shared by\n * the engine, the settings card, and the panel. Known id patterns map onto the\n * OpenAI-compatible image protocol families this plugin shapes requests for;\n * anything unrecognized falls back to the generic OpenAI protocol (best\n * effort) and is flagged as unknown so the UI can warn without blocking.\n *\n * Framework-free (pure data + regex), safe for the client bundle to inline.\n */\n\nexport type ModelFamily = 'gpt-image' | 'dall-e' | 'grok' | 'nanobanana' | 'seedream' | 'zhipu' | 'qwen' | 'minimax' | 'unknown'\n\n/** Capability/identity annotation for one model id. */\nexport interface ModelCatalogEntry {\n /** Family the request is shaped for. */\n family: ModelFamily\n /** Short badge label (en). */\n label: string\n /** Short badge label (zh). */\n labelZh: string\n /** Whether the family is known (false = best-effort OpenAI protocol). */\n known: boolean\n /** Whether the family supports image-to-image edits. */\n supportsEdit: boolean\n /** Whether the family natively takes aspect-ratio dials. */\n supportsAspectRatio: boolean\n /** Quality tiers the family interprets natively. */\n qualityTiers: string[]\n}\n\nconst ENTRIES: Record<Exclude<ModelFamily, 'unknown'>, Omit<ModelCatalogEntry, 'family'>> = {\n 'gpt-image': {\n label: 'gpt-image',\n labelZh: 'GPT 图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: false,\n qualityTiers: ['1K', '2K', '4K'],\n },\n 'dall-e': {\n label: 'DALL·E',\n labelZh: 'DALL·E',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: false,\n qualityTiers: ['auto'],\n },\n grok: {\n label: 'grok',\n labelZh: 'Grok',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K'],\n },\n nanobanana: {\n label: 'nanobanana',\n labelZh: 'Nano Banana',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K', '4K'],\n },\n seedream: {\n label: 'seedream',\n labelZh: 'Seedream',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['1K', '2K'],\n },\n zhipu: {\n label: 'GLM-Image',\n labelZh: '智谱图像',\n known: true,\n supportsEdit: false,\n supportsAspectRatio: false,\n qualityTiers: ['HD'],\n },\n qwen: {\n label: 'qwen-image',\n labelZh: '千问图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['auto'],\n },\n minimax: {\n label: 'MiniMax',\n labelZh: 'MiniMax 图像',\n known: true,\n supportsEdit: true,\n supportsAspectRatio: true,\n qualityTiers: ['auto'],\n },\n}\n\n/** Documented upstream prompt hard limits (UTF-16 code units). The engine\n * fast-fails on these before any network call and the panel counter shows\n * them live; families absent here have no documented limit. */\nconst PROMPT_CHAR_LIMITS: Partial<Record<Exclude<ModelFamily, 'unknown'>, number>> = {\n // MiniMax image-01 rejects prompts >= 1500 chars upstream (base_resp 2013).\n minimax: 1500,\n}\n\n/** The documented prompt character limit for a model id, or null when the\n * family has no known limit. Single source of truth for engine enforcement\n * and the panel's live counter. */\nexport function promptCharLimit(model: string): number | null {\n const { family } = describeModel(model)\n return (family !== 'unknown' ? PROMPT_CHAR_LIMITS[family] : undefined) ?? null\n}\n\n/** Official Gemini image ids served by Nano Banana gateways. */\nconst NANOBANANA_GEMINI_IDS = new Set([\n 'gemini-3-pro-image',\n 'gemini-3-pro-image-preview',\n 'gemini-3.1-flash-image',\n 'gemini-3.1-flash-image-preview',\n 'gemini-3.1-flash-lite-image',\n 'gemini-2.5-flash-image',\n])\n\n/** Classify one upstream model id into its request-shaping family. */\nexport function describeModel(model: string): ModelCatalogEntry {\n const id = model.trim()\n if (/^gpt-image/i.test(id)) return { family: 'gpt-image', ...ENTRIES['gpt-image'] }\n if (/^dall-e/i.test(id)) return { family: 'dall-e', ...ENTRIES['dall-e'] }\n if (/^grok-imagine(?:-|$)/.test(id)) return { family: 'grok', ...ENTRIES.grok }\n if (/^nanobanana/i.test(id) || NANOBANANA_GEMINI_IDS.has(id)) return { family: 'nanobanana', ...ENTRIES.nanobanana }\n if (/^(?:doubao-)?seedream/i.test(id)) return { family: 'seedream', ...ENTRIES.seedream }\n if (/^(?:glm-image|cogview(?:-|$))/i.test(id)) return { family: 'zhipu', ...ENTRIES.zhipu }\n if (/^qwen-image(?:[-_.]|$)/i.test(id)) return { family: 'qwen', ...ENTRIES.qwen }\n if (/^(?:minimax[-_/])?image-\\d+/i.test(id)) return { family: 'minimax', ...ENTRIES.minimax }\n return { family: 'unknown', label: 'unknown', labelZh: '未知协议', known: false, supportsEdit: true, supportsAspectRatio: false, qualityTiers: [] }\n}\n\n/** Conservative fallback for providers whose /models response only has ids.\n * Metadata-aware filtering lives in prompt-enhancer.ts; this catches common\n * image model naming conventions without treating every unknown model as an\n * image model. */\nexport function isLikelyImageModelId(model: string): boolean {\n return /(?:^|[-_.])(?:image|img|diffusion|flux|cogview|imagen|seedream|nanobanana|grok-imagine|dall-e|stable-diffusion|sdxl|pixart|kolors|ideogram|midjourney|recraft|hunyuan|jimeng|wanx|hidream|playground)(?:$|[-_.])/i.test(model.trim())\n}\n\n/** The family a model id routes its request through. */\nexport function modelFamily(model: string): ModelFamily {\n return describeModel(model).family\n}\n","/**\n * Wire contract shared by the host and client halves of dsh-imagegen: the\n * settings namespace, the route paths, and the generate payload/result shapes.\n * Pure types + constants 鈥?safe for the client bundle to inline.\n */\n\n/** Settings namespace this plugin owns (host settings seam + bridge). */\nexport const IMAGEGEN_SETTINGS_NAMESPACE = 'imagegen'\n\n/** Published package version shared by the host updater and the client UI. */\nexport const PLUGIN_VERSION = '1.5.13'\n\n/** Same-origin route family (fenced by the composition's connection service). */\nexport const SETTINGS_API = {\n describe: '/api/dsh-imagegen/settings/describe',\n mutate: '/api/dsh-imagegen/settings/mutate',\n} as const\n\n/** Subscription account login and status routes. */\nexport const SUBSCRIPTION_API = {\n status: '/api/dsh-imagegen/subscription/status',\n login: '/api/dsh-imagegen/subscription/login',\n} as const\n\n/** Official/subscription-backed image providers. */\nexport const SUBSCRIPTION_PROVIDERS = ['chatgpt-sub', 'grok-sub', 'google-sub', 'openrouter-sub'] as const\nexport type SubscriptionProvider = typeof SUBSCRIPTION_PROVIDERS[number]\n\n/** Transport protocol a channel uses to reach its gateway. */\nexport type ChannelProtocol = 'images' | 'chat-completions'\n\n/** Stored preference: auto keeps old channels on images unless the URL is explicit. */\nexport type ChannelProtocolPreference = 'auto' | ChannelProtocol\n\n/** Whether an endpoint URL explicitly names an OpenAI-compatible chat route. */\nexport function isChatCompletionsUrl(value: string): boolean {\n const trimmed = value.trim()\n if (trimmed === '') return false\n try {\n return /\\/chat\\/completions\\/?$/.test(new URL(trimmed).pathname)\n } catch {\n return /\\/chat\\/completions\\/?$/.test(trimmed.split(/[?#]/, 1)[0] ?? '')\n }\n}\n\n/** Whether a stored value is one of the accepted protocol preferences. */\nexport function isChannelProtocolPreference(value: unknown): value is ChannelProtocolPreference {\n return value === 'auto' || value === 'images' || value === 'chat-completions'\n}\n\n/** Resolve a channel preference without changing legacy behavior by default. */\nexport function resolveChannelProtocol(apiUrl: string, preference: ChannelProtocolPreference | undefined = 'auto'): ChannelProtocol {\n if (preference === 'images' || preference === 'chat-completions') return preference\n return isChatCompletionsUrl(apiUrl) ? 'chat-completions' : 'images'\n}\n\n/** Subscription channels with a fixed image model. */\nexport const DEFAULT_SUBSCRIPTION_MODELS: Record<SubscriptionProvider, string> = {\n 'chatgpt-sub': 'gpt-image-2.5-flare',\n 'grok-sub': 'grok-imagine-image-2.0',\n 'google-sub': 'gemini-3-pro-image',\n 'openrouter-sub': 'google/gemini-3-pro-image',\n}\n\n/** Human-facing provider names used in status and errors. */\nexport const SUBSCRIPTION_PROVIDER_DISPLAY_NAMES: Record<SubscriptionProvider, string> = {\n 'chatgpt-sub': 'ChatGPT 订阅',\n 'grok-sub': 'Grok 订阅',\n 'google-sub': 'Google 订阅',\n 'openrouter-sub': 'OpenRouter 账号',\n}\n\n/** Interfaces that are supported experimentally and should be labelled in the UI. */\nexport const EXPERIMENTAL_SUBSCRIPTION_PROVIDERS: ReadonlySet<SubscriptionProvider> = new Set(['chatgpt-sub', 'google-sub'])\n\n/** Whether a raw string names one of the subscription providers. */\nexport function isSubscriptionProvider(value: unknown): value is SubscriptionProvider {\n return typeof value === 'string' && (SUBSCRIPTION_PROVIDERS as readonly string[]).includes(value)\n}\n\n/** The image-generation proxy route. */\nexport const GENERATE_API = '/api/dsh-imagegen/generate'\n\n/** Host-mediated OpenAI-compatible prompt enhancement endpoints. */\nexport const PROMPT_ENHANCE_API = {\n models: '/api/dsh-imagegen/prompt-enhance/models',\n enhance: '/api/dsh-imagegen/prompt-enhance',\n} as const\n\n/** Host-mediated candidate discovery for the configured image API. */\nexport const IMAGE_MODEL_API = {\n models: '/api/dsh-imagegen/image-models',\n} as const\n\n/** Host-served built-in provider catalog (channels the user can instantiate). */\nexport const PRESETS_API = '/api/dsh-imagegen/presets' as const\n\n/** Image reader for Agent tool-result previews (fenced by the connection service). */\nexport const AGENT_IMAGE_API = '/api/dsh-imagegen/agent-image' as const\n\n/** Store the current composer image for the direct edit_image command. */\nexport const CONVERSATION_IMAGE_API = '/api/dsh-imagegen/conversation-image' as const\n\n/** Host-resident generation queue endpoints. */\nexport const TASK_API = {\n submit: '/api/dsh-imagegen/tasks/submit',\n list: '/api/dsh-imagegen/tasks/list',\n get: '/api/dsh-imagegen/tasks/get',\n cancel: '/api/dsh-imagegen/tasks/cancel',\n retry: '/api/dsh-imagegen/tasks/retry',\n} as const\n\n/** Generation modes. */\nexport type GenerateMode = 'text' | 'edit'\n\n/** A client → host generate request (what the panel collects). */\nexport interface GenerateRequest {\n /** text-to-image (images/generations) or image-to-image (images/edits). */\n mode: GenerateMode\n /**\n * User-facing model name (an alias from the channel's model catalog). The\n * host maps it onto the configured channel and fills `upstream` with the\n * real id before the engine sees it.\n */\n model: string\n /** The prompt. Upstream providers may impose their own length limits. */\n prompt: string\n /** Canvas size as an aspect ratio: 'auto' or e.g. '1:1' / '16:9' / '21:9'.\n * The host maps it onto each model's own vocabulary (aspect_ratio for Grok\n * and Nano Banana, resolution-tier size for Seedream, the closest pixel size for\n * OpenAI-compatible endpoints). */\n size: string\n /** Clarity tier: 'auto' | '1k' | '2k' | '4k'. The host maps it onto the\n * model's own vocabulary (resolution for Grok, image_size for Nano Banana,\n * and size for Seedream,\n * Nano Banana, quality for OpenAI). */\n quality: string\n /** Number of images, 1-4. */\n n: number\n /**\n * Passthrough detail parameter: '' (omit), 'standard', or 'high'. Some\n * gpt-image-2 gateways expose it; official OpenAI endpoints reject unknown\n * parameters, so the UI defaults to '' (omit).\n */\n detail: string\n /** Reference image as a data URL (edit mode only). */\n image?: string\n /** Additional reference images as data URLs (edit mode only). The first\n * image stays in `image`; providers that accept several references get them\n * all, single-reference providers see `image` alone. */\n images?: string[]\n /** Original reference-image name, retained in the history entry. */\n refName?: string\n /** Channel this request targets (the host falls back to the default when\n * absent, and re-routes by model alias when the alias lives elsewhere). */\n channelId?: string\n /** Channel display name snapshot, kept on the history entry (host-filled). */\n channel?: string\n /** Upstream model id actually sent to the gateway (host-filled from the\n * alias mapping; defaults to `model` when absent). */\n upstream?: string\n}\n\n/** One generated image, normalized host-side to base64 so the browser never\n * has to fetch the upstream (no CORS, no key exposure). */\nexport interface GeneratedImage {\n /** Raw base64 payload (no data: prefix). */\n b64: string\n /** MIME type of the payload, e.g. image/png. */\n mime: string\n /** Upstream revised prompt, when provided. */\n revisedPrompt?: string\n}\n\n/** Successful generate outcome. */\nexport interface GenerateResult {\n images: GeneratedImage[]\n}\n\n/**\n * One model mapping in a channel's catalog: the display alias the user, the\n * panel, and the Agent see, and the upstream model id actually sent to the\n * gateway. The alias defaults to the upstream id but can be renamed freely.\n */\nexport interface ModelMapping {\n /** User-facing model name (defaults to the upstream id). */\n alias: string\n /** Upstream model id sent to the gateway. */\n id: string\n}\n\n/**\n * One configured image channel (provider). Secrets never live here — the API\n * key is a credential (`DSH_IMAGEGEN_CHANNEL_<ID>`, see `channelKeyRef` in\n * `index.ts`), so a whole-array write can never clobber keys the user did not\n * re-enter and no key ever reaches `cordis.patch.yml`.\n */\nexport interface ChannelConfig {\n /** Stable channel id (the credential ref is derived from it). */\n id: string\n /** Preset provider id this channel was created from ('' = custom). */\n preset: string\n /** Display name shown in the list, the panel, and Agent guidance. */\n name: string\n /** OpenAI-compatible base URL, or the exact generation URL when apiUrlFull is true. */\n apiUrl: string\n /** Use apiUrl verbatim instead of appending /images/generations or /images/edits. */\n apiUrlFull: boolean\n /** Request protocol; 'auto' infers chat-completions from an exact chat URL. */\n protocol?: ChannelProtocolPreference\n /** Authentication source; absent means the legacy API-key path. */\n auth?: 'api-key' | 'subscription'\n /** Subscription provider when auth is subscription. */\n subscription?: SubscriptionProvider\n /** The channel's model catalog (alias → upstream id). */\n models: ModelMapping[]\n}\n\n/** One built-in provider as the settings card consumes it. */\nexport interface PresetProviderView {\n id: string\n name: string\n apiUrl: string\n hint: string\n models: ModelMapping[]\n /** Set for subscription-backed presets. */\n subscription?: SubscriptionProvider\n /** True when the subscription relies on an undocumented interface. */\n experimental?: boolean\n}\n\nexport type GenerationTaskStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'\n\nexport interface GenerationTask {\n id: string\n request: GenerateRequest\n status: GenerationTaskStatus\n createdAt: number\n startedAt?: number\n finishedAt?: number\n result?: GenerateResult\n error?: string\n}\n\n/**\n * Wire shape for the queue poll. Reference-image and result payloads stay\n * host-side; the client hydrates a completed task once through TASK_API.get.\n */\nexport interface GenerationTaskSummary extends Omit<GenerationTask, 'result'> {\n resultAvailable: boolean\n}\n","/**\n * React binding for the plugin locale, which follows the DSH interface\n * language (bridged from ctx.locale in client/index.ts). Surfaces that render\n * tt() output subscribe to this tick so a DSH language switch re-renders them\n * immediately.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport { getImageGenLanguageVersion, subscribeImageGenLanguage } from './helpers.ts'\n\n/** Re-render the calling component whenever the DSH language changes. */\nexport function useImageGenLanguageTick(): number {\n return useSyncExternalStore(subscribeImageGenLanguage, getImageGenLanguageVersion)\n}\n","/**\n * The dsh-imagegen settings card: channel management (list rows with status\n * dots, an editor dialog with the model-catalog alias → upstream mapping, and\n * built-in provider presets), plus the prompt-enhancement model and the plugin\n * switches. Registers as the row's configuration entry on the Plugins page\n * (`plugins.row.config`), independent of the dsh-web-ui family group, bound to\n * the plugin's own bridge settings scope.\n *\n * The interaction mirrors the host's model-provider page: one row per channel\n * (status dot + edit/delete), two add buttons (built-in provider / custom),\n * and an editor holding API key, display name, API URL, and the model catalog\n * with detection.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { CardForm, booleanField, secretField, textField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport { ChannelsForm, type ChannelDraft, type ChannelsFormActions, type ChannelsFormState } from './channels-form.ts'\nimport type { ImageGenScope } from './settings-scope.ts'\nimport { describeModel } from '../model-catalog.ts'\nimport { IMAGE_MODEL_API, PRESETS_API, PROMPT_ENHANCE_API, type ModelMapping, type PresetProviderView } from '../protocol.ts'\nimport type { ImageGenKey } from './locales.ts'\nimport { tt, type TranslateValues } from './helpers.ts'\nimport { useImageGenLanguageTick } from './use-language.ts'\nimport css from './settings-card.module.css'\n\n/** The global (non-channel) fields this card's staged form edits. */\nexport interface ImageGenSettings {\n enabled?: boolean\n announceToAgent?: boolean\n allowAgentImageGeneration?: boolean\n promptApiUrl?: string\n promptApiKey?: string\n promptModel?: string\n}\n\n/** What the card renders. */\nexport interface ImageGenSettingsCardState extends CardShell {\n /** Channel list staging (channels + per-channel key edits + default). */\n channels: ChannelsFormState\n /** Master switch. */\n enabled: CardFieldState\n /** System-prompt announcement flag. */\n announceToAgent: CardFieldState\n allowAgentImageGeneration: CardFieldState\n promptApiUrl: CardFieldState\n promptApiKey: CardFieldState\n promptModel: CardFieldState\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface ImageGenSettingsCardFace extends CardActions {\n /** Channel staging actions (committed together with the card's save). */\n channels: ChannelsFormActions\n hooks: {\n /** Card snapshot bound by the renderer as useImageGenSettingsCard. */\n imageGenSettingsCard: SnapshotStore<ImageGenSettingsCardState>\n }\n}\n\n/** Bridges the imagegen scope onto the card's staged forms. */\nexport class ImageGenSettingsCardController {\n private readonly form: CardForm<ImageGenSettings>\n private readonly channelsForm: ChannelsForm\n\n /** @param scope - the bound bridge scope for the dsh-imagegen namespace. */\n constructor(private readonly scope: ImageGenScope) {\n this.form = new CardForm(scope, [\n booleanField('enabled'),\n booleanField('announceToAgent'),\n booleanField('allowAgentImageGeneration'),\n textField('promptApiUrl'),\n secretField('promptApiKey'),\n textField('promptModel'),\n ], {\n secretSettled: (field) => this.scope.getSecretSetSnapshot(field),\n })\n this.channelsForm = new ChannelsForm(scope)\n }\n\n private projection(): ImageGenSettingsCardState {\n const shell = this.form.shell()\n return {\n ...shell,\n dirty: shell.dirty || this.channelsForm.snapshot().dirty,\n channels: this.channelsForm.snapshot(),\n enabled: this.form.field('enabled'),\n announceToAgent: this.form.field('announceToAgent'),\n allowAgentImageGeneration: this.form.field('allowAgentImageGeneration'),\n promptApiUrl: this.form.field('promptApiUrl'),\n promptApiKey: this.form.field('promptApiKey'),\n promptModel: this.form.field('promptModel'),\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and the form/channel actions.\n */\n inject(): ImageGenSettingsCardFace {\n const cardStore = this.form.bind(() => this.projection())\n this.channelsForm.subscribe(() => { cardStore.set(this.projection()) })\n return {\n hooks: {\n imageGenSettingsCard: cardStore,\n },\n channels: this.channelsForm.actions(),\n ...this.form.actions(),\n }\n }\n}\n\n/** Props the renderer binds for this card. */\nexport type ImageGenSettingsCardProps =\n PropsRuntime<'plugins.row.config'>\n & PropsLocale<'dsh-imagegen'>\n & InjectFace<ImageGenSettingsCardFace>\n\n/**\n * Render the card.\n *\n * The Plugins page renders every configuration entry twice: once as the\n * one-liner under the row's title (`view: 'summary'`) and once as the body of\n * the row's own page (`view: 'page'`). The page draws the row id, the module\n * name, and the crumb itself, so this component supplies only the copy and the\n * form. The summary branch returns before the form's hooks and state so the\n * one-liner does no work.\n *\n * @param props - the view asked for, locale copy, the card snapshot, and the form actions.\n * @returns the one-liner, the card, or nothing while the namespace is loading.\n */\nexport function ImageGenSettingsCard(props: ImageGenSettingsCardProps) {\n // The summary view is the one-liner the page places under the row's title.\n // It returns before any hook or state so it does no work: the page re-renders\n // the entry when the active locale moves, so the copy still follows a\n // language switch without this component subscribing to it.\n if (props.view === 'summary') return tt('settings.summary')\n // The card renders through the plugin's own dictionary so the uiLanguage\n // override applies here too — the host-locale props.t would only follow the\n // DSH interface language.\n const t = tt\n useImageGenLanguageTick()\n const state = props.useImageGenSettingsCard(snapshot => snapshot)\n const [open, setOpen] = useState(false)\n // Global-section local states (prompt enhancement etc.).\n const [promptModels, setPromptModels] = useState<string[]>([])\n const [loadingPromptModels, setLoadingPromptModels] = useState(false)\n const [promptModelsError, setPromptModelsError] = useState<string | null>(null)\n const [manualPromptModelOpen, setManualPromptModelOpen] = useState(false)\n const [manualPromptModel, setManualPromptModel] = useState('')\n const [enhancementOpen, setEnhancementOpen] = useState(false)\n const [promptApiOpen, setPromptApiOpen] = useState(false)\n const [moreOpen, setMoreOpen] = useState(false)\n // Channel list local states.\n const [editingId, setEditingId] = useState<string | null>(null)\n const [presetPickerOpen, setPresetPickerOpen] = useState(false)\n const [presets, setPresets] = useState<PresetProviderView[]>([])\n const [presetError, setPresetError] = useState<string | null>(null)\n const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null)\n\n if (!state.available) return null\n const title = t('settings.title')\n const blocked = !state.dirty || state.invalid || state.saving || state.channels.saving\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n\n if (!state.exposed) {\n return (\n <li className={css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{t('settings.description')}</span>\n </span>\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n\n const channels = state.channels.channels\n const editing = editingId === null ? undefined : channels.find(channel => channel.id === editingId)\n\n return (\n <li className={css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{t('settings.description')}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{t('settings.unsaved')}</span> : null}\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{t('settings.readOnly')}</p> : null}\n\n <section className={css.channelSection} aria-label={t('channels.title')}>\n <div className={css.sectionHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.title')}</h3>\n <p className={css.sectionHint}>{t('channels.hint')}</p>\n </div>\n </div>\n {channels.length === 0\n ? <p className={css.channelEmpty}>{t('channels.empty')}</p>\n : (\n <ul className={css.channelList}>\n {channels.map(channel => {\n const keyHeld = state.channels.keySet[channel.id] === true\n const ready = keyHeld && channel.models.length > 0\n const isDefault = channel.id === state.channels.defaultChannelId\n if (confirmDeleteId === channel.id) {\n return (\n <li key={channel.id} className={css.channelRow} data-action>\n <span className={css.deleteConfirmText}>{t('channels.deleteConfirmTitle', { name: channel.name || t('channels.untitled') })}</span>\n <button type=\"button\" className={css.channelDanger} disabled={disabled} onClick={() => { props.channels.setChannels(channels.filter(candidate => candidate.id !== channel.id)); if (isDefault && channels.length > 1) { const next = channels.find(candidate => candidate.id !== channel.id); if (next !== undefined) props.channels.setDefaultChannel(next.id) } setConfirmDeleteId(null); if (editingId === channel.id) setEditingId(null) }}>{t('channels.confirm')}</button>\n <button type=\"button\" className={css.channelAction} disabled={disabled} onClick={() => { setConfirmDeleteId(null) }}>{t('channels.cancel')}</button>\n </li>\n )\n }\n return (\n <li key={channel.id} className={css.channelRow}>\n <span className={ready ? css.channelDotReady : css.channelDotWarn} aria-hidden=\"true\" title={t(ready ? 'channels.statusReady' : 'channels.statusIncomplete')} />\n <button type=\"button\" className={css.channelMain} disabled={disabled} onClick={() => { setEditingId(channel.id) }}>\n <span className={css.channelName}>{isDefault ? `★ ${channel.name || t('channels.untitled')}` : (channel.name || t('channels.untitled'))}</span>\n <span className={css.channelMeta}>\n <span className={css.channelBadge} data-warn={!keyHeld || channel.models.length === 0 ? '' : undefined}>\n {keyHeld ? t('channels.keySet') : t('channels.keyMissing')}\n {' · '}\n {channel.models.length > 0 ? t('channels.modelCount', { n: channel.models.length }) : t('channels.noModels')}\n </span>\n </span>\n </button>\n <button type=\"button\" className={css.channelAction} onClick={() => { setEditingId(channel.id) }}>{t('channels.edit')}</button>\n <button type=\"button\" className={css.channelAction} data-danger onClick={() => { setConfirmDeleteId(channel.id) }}>{t('channels.delete')}</button>\n </li>\n )\n })}\n </ul>\n )}\n <div className={css.channelControls}>\n {open && presetPickerOpen ? (\n <PresetPicker\n t={t}\n presets={presets}\n error={presetError}\n disabled={state.writable === false}\n onLoad={() => {\n setPresetError(null)\n void fetch(PRESETS_API, { method: 'POST' })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; presets?: PresetProviderView[]; message?: string }\n if (!response.ok || body.ok !== true || body.presets === undefined) throw new Error(body.message ?? `HTTP ${response.status}`)\n setPresets(body.presets)\n })\n .catch(error => { setPresetError(error instanceof Error ? error.message : String(error)) })\n }}\n onPick={(preset) => {\n const draft = newChannelDraft(preset)\n props.channels.setChannels([...channels, draft])\n setPresetPickerOpen(false)\n setEditingId(draft.id)\n }}\n onCustom={() => {\n const draft = newChannelDraft(undefined)\n props.channels.setChannels([...channels, draft])\n setPresetPickerOpen(false)\n setEditingId(draft.id)\n }}\n onClose={() => { setPresetPickerOpen(false) }}\n />\n ) : null}\n\n <div className={css.channelAddRow}>\n <button type=\"button\" className={css.channelAdd} disabled={disabled} onClick={() => { setPresetError(null); setPresetPickerOpen(true) }}>+ {t('channels.addProvider')}</button>\n <button type=\"button\" className={css.channelAdd} disabled={disabled} onClick={() => { addCustomChannel(channels, props.channels, setEditingId) }}>+ {t('channels.addCustom')}</button>\n </div>\n </div>\n </section>\n\n <button\n type=\"button\"\n className={css.disclosure}\n aria-expanded={enhancementOpen}\n onClick={() => { setEnhancementOpen(open => !open) }}\n >\n <span>{t('settings.promptEnhanceTitle')}</span>\n <span>{t('settings.optional')}</span>\n <span aria-hidden=\"true\">{enhancementOpen ? '⌃' : '⌄'}</span>\n </button>\n {enhancementOpen ? <section className={css.optionalContent} aria-label={t('settings.promptEnhanceTitle')}>\n <p className={css.sectionHint}>{t('settings.promptEnhanceHint')}</p>\n <div className={css.sectionHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('settings.promptModel')}</h3>\n <p className={css.sectionHint}>{t('settings.promptModelDetectionHint')}</p>\n </div>\n <button\n type=\"button\"\n className={css.modelFetch}\n disabled={disabled || loadingPromptModels}\n onClick={() => {\n setLoadingPromptModels(true)\n setPromptModelsError(null)\n void fetch(PROMPT_ENHANCE_API.models, { method: 'POST' })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; models?: string[]; message?: string }\n if (!response.ok || body.ok !== true) throw new Error(body.message ?? `HTTP ${response.status}`)\n setPromptModels(body.models ?? [])\n })\n .catch(error => { setPromptModelsError(error instanceof Error ? error.message : String(error)) })\n .finally(() => { setLoadingPromptModels(false) })\n }}\n >\n {loadingPromptModels ? t('settings.promptModelsLoading') : t('settings.promptModelsFetch')}\n </button>\n </div>\n <div className={css.modelSummary}>\n {state.promptModel.text.trim() !== '' ? (\n <span className={css.modelChip}>\n <span>{state.promptModel.text}</span>\n <button type=\"button\" disabled={disabled} aria-label={`${t('settings.removeModel')}: ${state.promptModel.text}`} onClick={() => { props.edit('promptModel', '') }}>×</button>\n </span>\n ) : null}\n <button type=\"button\" className={css.addModel} disabled={disabled} onClick={() => { setManualPromptModelOpen(open => !open); setEnhancementOpen(true) }}>\n {manualPromptModelOpen ? t('settings.cancelAddModel') : t('settings.addModel')}\n </button>\n </div>\n {manualPromptModelOpen ? (\n <div className={css.manualModelRow}>\n <input className={css.input} value={manualPromptModel} placeholder={t('settings.addPromptModelPlaceholder')} disabled={disabled} onChange={event => { setManualPromptModel(event.target.value) }} />\n <button type=\"button\" className={css.addModel} disabled={disabled || manualPromptModel.trim() === ''} onClick={() => { props.edit('promptModel', manualPromptModel); setManualPromptModel('') }}>{t('settings.addModelConfirm')}</button>\n </div>\n ) : null}\n {promptModels.length > 0 ? (\n <div className={css.modelCandidateList} role=\"radiogroup\" aria-label={t('settings.promptModelsCandidates')}>\n <span className={css.modelCandidateLabel}>{t('settings.promptModelsCandidates')}</span>\n {promptModels.map(candidate => (\n <button\n key={candidate}\n type=\"button\"\n role=\"radio\"\n className={css.modelCandidate}\n aria-checked={state.promptModel.text === candidate}\n data-selected={state.promptModel.text === candidate ? '' : undefined}\n disabled={disabled}\n onClick={() => { props.edit('promptModel', candidate) }}\n >\n {candidate}\n </button>\n ))}\n </div>\n ) : null}\n {promptModelsError !== null ? <p className={css.failed} role=\"status\">{promptModelsError}</p> : null}\n <button type=\"button\" className={css.inlineDisclosure} aria-expanded={promptApiOpen} onClick={() => { setPromptApiOpen(open => !open) }}>\n <span>{t('settings.promptApiAdvanced')}</span>\n <span aria-hidden=\"true\">{promptApiOpen ? '⌃' : '⌄'}</span>\n </button>\n {promptApiOpen ? <div className={css.optionalContent}>\n <ValueField\n id=\"dsh-imagegen-settings-prompt-apiurl\"\n label={t('settings.promptApiUrl')}\n hint={t('settings.promptApiUrlHint')}\n placeholder=\"https://api.openai.com/v1\"\n {...fieldProps}\n {...state.promptApiUrl}\n onEdit={(text) => { props.edit('promptApiUrl', text) }}\n onReset={() => { props.resetField('promptApiUrl') }}\n />\n <ValueField\n id=\"dsh-imagegen-settings-prompt-apikey\"\n label={t('settings.promptApiKey')}\n hint={t('settings.promptApiKeyHint')}\n placeholder=\"sk-…\"\n secret\n {...fieldProps}\n {...state.promptApiKey}\n overridden={false}\n onEdit={(text) => { props.edit('promptApiKey', text) }}\n onReset={() => { props.resetField('promptApiKey') }}\n />\n </div> : null}\n </section> : null}\n\n <button type=\"button\" className={css.disclosure} aria-expanded={moreOpen} onClick={() => { setMoreOpen(open => !open) }}>\n <span>{t('settings.moreOptions')}</span>\n <span aria-hidden=\"true\">{moreOpen ? '⌃' : '⌄'}</span>\n </button>\n {moreOpen ? <div className={css.optionalContent}>\n <BooleanField\n id=\"dsh-imagegen-settings-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <BooleanField\n id=\"dsh-imagegen-settings-announce\"\n label={t('settings.announceToAgent')}\n hint={t('settings.announceToAgentHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.announceToAgent}\n onEdit={(text) => { props.edit('announceToAgent', text) }}\n onReset={() => { props.resetField('announceToAgent') }}\n />\n <BooleanField\n id=\"dsh-imagegen-settings-agent-generation\"\n label={t('settings.allowAgentImageGeneration')}\n hint={t('settings.allowAgentImageGenerationHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.allowAgentImageGeneration}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n </div> : null}\n <div className={css.footer}>\n {(state.failed || state.channels.failed) ? <p className={css.failed} role=\"status\">{t('settings.saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving || state.channels.saving}\n onClick={() => { props.discard(); props.channels.discard() }}\n >\n {t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={() => { void props.channels.commit(); void props.save() }}\n >\n {t(!state.saving && !state.channels.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n </div>\n )\n : null}\n\n {open && editing !== undefined ? (\n <ChannelEditor\n key={editing.id}\n t={t}\n channel={editing}\n keyHeld={state.channels.keySet[editing.id] === true}\n otherChannels={channels.filter(channel => channel.id !== editing.id)}\n isDefault={editing.id === state.channels.defaultChannelId}\n writable={state.writable}\n onPatch={(patch) => { replaceChannel(channels, editing.id, patch, props.channels) }}\n onSetModels={(models) => { props.channels.setChannels(channels.map(channel => channel.id === editing.id ? { ...channel, models } : channel)) }}\n onSetKey={(value) => { props.channels.setChannelKey(editing.id, value) }}\n onSetDefault={() => { props.channels.setDefaultChannel(editing.id) }}\n onRemove={() => { props.channels.setChannels(channels.filter(channel => channel.id !== editing.id)); if (editing.id === state.channels.defaultChannelId && channels.length > 1) { const next = channels.find(channel => channel.id !== editing.id); if (next !== undefined) props.channels.setDefaultChannel(next.id) } setEditingId(null) }}\n onClose={() => { setEditingId(null) }}\n />\n ) : null}\n\n </li>\n )\n}\n\n/** Channel row + dialog helpers -------------------------------------------------- */\n\nfunction newChannelDraft(preset: PresetProviderView | undefined): ChannelDraft {\n return {\n id: clientId(),\n preset: preset?.id ?? '',\n name: preset?.name ?? '',\n apiUrl: preset?.apiUrl ?? '',\n apiUrlFull: false,\n models: (preset?.models ?? []).map(model => ({ ...model })),\n }\n}\n\nfunction addCustomChannel(channels: ChannelDraft[], form: ChannelsFormActions, openEditor: (id: string) => void): void {\n const draft = newChannelDraft(undefined)\n form.setChannels([...channels, draft])\n openEditor(draft.id)\n}\n\n/** Patch one field (or models) of one staged channel. */\nfunction replaceChannel(channels: ChannelDraft[], id: string, patch: Partial<ChannelDraft>, form: ChannelsFormActions): void {\n form.setChannels(channels.map(channel => channel.id === id ? { ...channel, ...patch } : channel))\n}\n\nfunction clientId(): string {\n const random = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : undefined\n return random ?? `ch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/** Built-in provider picker, expanded inside the settings card. */\nfunction PresetPicker(props: {\n t: (key: ImageGenKey, params?: Record<string, string | number>) => string\n presets: PresetProviderView[]\n error: string | null\n disabled: boolean\n onLoad: () => void\n onPick: (preset: PresetProviderView) => void\n onCustom: () => void\n onClose: () => void\n}) {\n const { t } = props\n const loadedRef = useRef(false)\n useEffect(() => {\n if (loadedRef.current) return\n loadedRef.current = true\n props.onLoad()\n }, [])\n return (\n <section className={css.presetInline} aria-label={t('channels.presetPickerTitle')}>\n <header className={css.presetInlineHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.presetPickerTitle')}</h3>\n <p className={css.sectionHint}>{t('channels.presetPickerHint')}</p>\n </div>\n <button type=\"button\" className={css.editorClose} aria-label={t('preview.close')} onClick={props.onClose}>×</button>\n </header>\n <div className={css.presetList}>\n {props.presets.map(preset => (\n <button key={preset.id} type=\"button\" className={css.presetRow} disabled={props.disabled} onClick={() => { props.onPick(preset) }}>\n <span className={css.presetName}>{preset.name}</span>\n <span className={css.presetMeta}>{preset.models.map(model => model.alias).join(' · ')}</span>\n </button>\n ))}\n <button type=\"button\" className={css.presetRow} data-custom disabled={props.disabled} onClick={props.onCustom}>\n <span className={css.presetName}>+ {t('channels.addCustom')}</span>\n <span className={css.presetHint}>{t('channels.presetCustomHint')}</span>\n </button>\n {props.error !== null ? <p className={css.failed} role=\"status\">{t('channels.presetLoadFailed', { error: props.error })}</p> : null}\n </div>\n </section>\n )\n}\n\n/** Channel editor (modal): key, display name, API URL, model catalog. */\nfunction ChannelEditor(props: {\n t: (key: ImageGenKey, params?: Record<string, string | number>) => string\n channel: ChannelDraft\n keyHeld: boolean\n otherChannels: ChannelDraft[]\n isDefault: boolean\n writable: boolean\n onPatch: (patch: Partial<ChannelDraft>) => void\n onSetModels: (models: ModelMapping[]) => void\n onSetKey: (value: string | undefined) => void\n onSetDefault: () => void\n onRemove: () => void\n onClose: () => void\n}) {\n const { t, channel } = props\n const [keyDraft, setKeyDraft] = useState('')\n const [candidates, setCandidates] = useState<string[] | null>(null)\n const [detecting, setDetecting] = useState(false)\n const [detectError, setDetectError] = useState<string | null>(null)\n const [manualId, setManualId] = useState('')\n const [removeOpen, setRemoveOpen] = useState(false)\n const [copyFrom, setCopyFrom] = useState('')\n\n const detect = (): void => {\n setDetecting(true)\n setDetectError(null)\n const payload: Record<string, unknown> = { channelId: channel.id }\n if (channel.apiUrl.trim() !== '') payload.apiUrl = channel.apiUrl.trim()\n if (keyDraft.trim() !== '') payload.apiKey = keyDraft.trim()\n void fetch(IMAGE_MODEL_API.models, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })\n .then(async response => {\n const body = await response.json() as { ok?: boolean; models?: string[]; message?: string }\n if (!response.ok || body.ok !== true) throw new Error(body.message ?? `HTTP ${response.status}`)\n setCandidates(body.models ?? [])\n })\n .catch(error => { setDetectError(error instanceof Error ? error.message : String(error)) })\n .finally(() => { setDetecting(false) })\n }\n\n // Auto-detect once when the dialog opens with a complete endpoint.\n const autoDetected = useRef(false)\n useEffect(() => {\n if (autoDetected.current) return\n autoDetected.current = true\n if (channel.apiUrl.trim() !== '' && (props.keyHeld || keyDraft.trim() !== '')) detect()\n }, [])\n\n const addManual = (): void => {\n const id = manualId.trim()\n if (id === '') return\n const next = [...channel.models]\n const alias = id\n if (!next.some(model => model.alias === alias)) next.push({ alias, id })\n props.onSetModels(next)\n setManualId('')\n }\n\n const copyFromChannel = (): void => {\n const source = props.otherChannels.find(chance => chance.id === copyFrom)\n if (source === undefined) return\n const merged = [...channel.models]\n for (const model of source.models) {\n const alias = model.alias\n // Copy with a collision suffix so both sources stay selectable.\n let unique = alias\n let suffix = 2\n while (merged.some(entry => entry.alias === unique)) unique = `${alias} (${suffix++})`\n merged.push({ alias: unique, id: model.id })\n }\n props.onSetModels(merged)\n setCopyFrom('')\n }\n\n return (\n <div className={css.editorBackdrop} role=\"dialog\" aria-modal=\"true\" aria-label={`${t('channels.editorTitle')} · ${channel.name || t('channels.untitled')}`} onClick={props.onClose}>\n <div className={css.editorPanel} onClick={event => { event.stopPropagation() }}>\n <header className={css.editorHeader}>\n <div>\n <h3 className={css.sectionTitle}>{t('channels.editorTitle')} · {channel.name || t('channels.untitled')}</h3>\n <p className={css.sectionHint}>{t('channels.editorSaveNote')}</p>\n </div>\n <button type=\"button\" className={css.editorClose} aria-label={t('preview.close')} onClick={props.onClose}>×</button>\n </header>\n\n <div className={css.editorField}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-name\">{t('channels.displayName')}</label>\n <input id=\"dsh-imagegen-channel-name\" className={css.input} value={channel.name} placeholder={t('channels.untitled')} disabled={!props.writable} onChange={event => { props.onPatch({ name: event.target.value }) }} />\n </div>\n <div className={css.editorField}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-url\">{t('channels.apiUrl')}</label>\n <input id=\"dsh-imagegen-channel-url\" className={css.input} value={channel.apiUrl} placeholder=\"https://api.example.com/v1\" disabled={!props.writable} onChange={event => { props.onPatch({ apiUrl: event.target.value }) }} />\n </div>\n <div className={css.editorField}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"dsh-imagegen-channel-key\">{t('channels.apiKey')}</label>\n {props.keyHeld || keyDraft !== ''\n ? (\n <button type=\"button\" className={css.reset} disabled={!props.writable} onClick={() => { setKeyDraft(''); props.onSetKey(undefined) }}>\n {t('channels.keyClear')}\n </button>\n )\n : null}\n </div>\n <input\n id=\"dsh-imagegen-channel-key\"\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={keyDraft}\n placeholder={props.keyHeld ? t('channels.keyReplaceHint') : t('channels.keyMissingHint')}\n disabled={!props.writable}\n onChange={event => { const value = event.target.value; setKeyDraft(value); props.onSetKey(value === '' ? undefined : value) }}\n />\n </div>\n\n <div className={css.editorDivider} />\n\n <div className={css.editorSectionHeader}>\n <h4 className={css.label}>{t('channels.modelCatalogTitle')}</h4>\n <button type=\"button\" className={css.modelFetch} disabled={!props.writable || detecting} onClick={detect}>\n {detecting ? t('channels.detecting') : t('channels.detect')}\n </button>\n </div>\n {detectError !== null ? <p className={css.failed} role=\"status\">{t('channels.detectFailed', { error: detectError })}</p> : null}\n {candidates !== null && detectError === null ? <p className={css.detectOk} role=\"status\">{t('channels.detectSuccess', { n: candidates.length })}</p> : null}\n\n {channel.models.length === 0\n ? <p className={css.sectionHint}>{t('channels.noModelsHint')}</p>\n : (\n <ul className={css.modelRows}>\n {channel.models.map((model, index) => {\n const entry = describeModel(model.id || model.alias)\n return (\n <li key={`${model.alias}-${index}`} className={css.modelRow}>\n <div className={css.modelRowInputs}>\n <input className={css.input} value={model.alias} aria-label={t('channels.modelAliasLabel')} disabled={!props.writable} onChange={event => {\n const next = [...channel.models]\n next[index] = { ...model, alias: event.target.value }\n props.onSetModels(next)\n }} />\n <span className={css.modelArrow}>→</span>\n <input className={css.input} value={model.id} aria-label={t('channels.modelIdLabel')} disabled={!props.writable} onChange={event => {\n const next = [...channel.models]\n next[index] = { ...model, id: event.target.value }\n props.onSetModels(next)\n }} />\n </div>\n <div className={css.modelRowBadges}>\n <span className={css.modelBadge}>{entry.labelZh}{entry.known ? '' : ` · ${t('channels.unknownProtocol')}`}</span>\n <button type=\"button\" className={css.modelRowRemove} disabled={!props.writable} aria-label={`${t('channels.removeModel')}: ${model.alias}`} onClick={() => { props.onSetModels(channel.models.filter((_, i) => i !== index)) }}>×</button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n\n <div className={css.editorTools}>\n <div className={css.manualModelRow}>\n <input className={css.input} value={manualId} placeholder={t('channels.manualAddPlaceholder')} disabled={!props.writable} onChange={event => { setManualId(event.target.value) }} onKeyDown={event => { if (event.key === 'Enter') { event.preventDefault(); addManual() } }} />\n <button type=\"button\" className={css.addModel} disabled={!props.writable || manualId.trim() === ''} onClick={addManual}>{t('channels.addModelConfirm')}</button>\n </div>\n {props.otherChannels.length > 0 ? (\n <div className={css.manualModelRow}>\n <select className={css.modelChoices} value={copyFrom} disabled={!props.writable} onChange={event => { setCopyFrom(event.target.value) }} aria-label={t('channels.copyFrom')}>\n <option value=\"\">{t('channels.copyFrom')}</option>\n {props.otherChannels.map(other => (\n <option key={other.id} value={other.id}>{other.name || t('channels.untitled')}</option>\n ))}\n </select>\n <button type=\"button\" className={css.addModel} disabled={!props.writable || copyFrom === ''} onClick={copyFromChannel}>{t('channels.copyApply')}</button>\n </div>\n ) : null}\n </div>\n\n {candidates !== null && candidates.length > 0 ? (\n <div className={css.modelCandidateList}>\n <span className={css.modelCandidateLabel}>\n {t('channels.candidatesTitle')}\n </span>\n {candidates.map(candidate => {\n const selected = channel.models.some(model => model.alias === candidate)\n const entry = describeModel(candidate)\n return (\n <label key={candidate} className={css.modelCandidate} data-selected={selected ? '' : undefined}>\n <input type=\"checkbox\" checked={selected} disabled={!props.writable} onChange={() => {\n const merged = selected\n ? channel.models.filter(model => model.alias !== candidate)\n : [...channel.models, { alias: candidate, id: candidate }]\n props.onSetModels(merged)\n }} />\n <span>{candidate}</span>\n {!entry.known ? <span className={css.modelBadge} data-warn>{t('channels.unknownProtocol')}</span> : <span className={css.modelBadge}>{entry.labelZh}</span>}\n </label>\n )\n })}\n </div>\n ) : null}\n\n <div className={css.editorDivider} />\n\n <div className={css.editorFooter}>\n {props.isDefault ? <span className={css.channelBadge} data-default>{t('channels.defaultLabel')}</span> : (\n <button type=\"button\" className={css.inlineDisclosure} disabled={!props.writable} onClick={props.onSetDefault}>{t('channels.setDefault')}</button>\n )}\n <span className={css.spacer} />\n {removeOpen\n ? (\n <>\n <button type=\"button\" className={css.channelDanger} disabled={!props.writable} onClick={props.onRemove}>{t('channels.confirm')}</button>\n <button type=\"button\" className={css.channelAction} onClick={() => { setRemoveOpen(false) }}>{t('channels.cancel')}</button>\n </>\n )\n : (\n <button type=\"button\" className={css.channelAction} data-danger onClick={() => { setRemoveOpen(true) }}>{t('channels.deleteThisChannel')}</button>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\ninterface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field; `secret` renders a password control. */\nfunction ValueField(props: FieldProps & {\n /** Render a password control. */\n secret?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n /** Label of the dedicated clear control (secret fields). */\n clearLabel?: string\n /** Stage a clear of the stored secret. */\n onClear?: () => void\n /** Whether a stored secret exists (enables the clear control). */\n canClear?: boolean\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n {props.secret === true && props.canClear === true\n ? (\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onClear}\n >\n {props.clearLabel ?? props.resetLabel}\n </button>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type={props.secret === true ? 'password' : 'text'}\n autoComplete={props.secret === true ? 'off' : undefined}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nfunction BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <select\n id={props.id}\n className={css.select}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n <option value=\"\">{props.inheritLabel}</option>\n <option value=\"true\">{props.onLabel}</option>\n <option value=\"false\">{props.offLabel}</option>\n </select>\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n","/**\n * Browser-side settings scope for the dsh-imagegen namespace, served by the\n * plugin's own same-origin bridge routes (/api/dsh-imagegen/settings). The\n * official rc.6 settings scope answers \"unavailable\" for every third-party\n * namespace (the host-apiproxy allowlist is hard-coded), so this package\n * re-serves its namespace through the host settings seam over a same-origin\n * HTTP pair — the same pattern the dsh-web-ui family bridge\n * uses, self-contained per plugin.\n */\n\nimport {\n createSnapshotStore,\n type SnapshotStore,\n} from '@deepseek-ai/dsh-client-store'\nimport type { SettingsPathOpView } from '@deepseek-ai/dsh-api-remotes/client'\nimport { IMAGEGEN_SETTINGS_NAMESPACE, SETTINGS_API, type ChannelConfig } from '../protocol.ts'\n\n/**\n * Client-side sync state of one settings namespace, owned here because this\n * package re-serves its namespace over its own bridge rather than consuming\n * the host scope service, and the host generation this bridge was written\n * against no longer exports the contract.\n */\nexport interface SettingsScopeSnapshot<T> {\n status: 'loading' | 'ready' | 'unavailable'\n value: T | undefined\n base: unknown\n user: unknown\n revision: number | undefined\n writable: boolean\n mode: 'host' | 'memory'\n}\n\n/** Reactive owner handle over one namespace's durable section. */\nexport interface SettingsScope<T> {\n getSnapshot(): SettingsScopeSnapshot<T>\n subscribe(listener: () => void): () => void\n mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<void>\n set(field: string, value: unknown): Promise<void>\n unset(field: string): Promise<void>\n}\n\n/** One cascading-picker group: a configured channel and its model aliases. */\nexport interface ImageModelGroup {\n /** Stable channel id; '' is the legacy single-channel fallback. */\n id: string\n /** Display name shown in the cascading model picker. */\n name: string\n /** Model aliases configured on this channel, in display order. */\n models: string[]\n}\n\n/** The fields this plugin's settings card edits. */\nexport interface ImageGenConfig {\n enabled?: boolean\n announceToAgent?: boolean\n allowAgentImageGeneration?: boolean\n /** Configured channels (each: name, endpoint, model catalog). */\n channels?: ChannelConfig[]\n /** Per-channel API keys, keyed by channel id. Write-only wire shape: the\n * host bridge diverts it into the credential store, so it never appears in\n * the settings value. Key presence comes from the secrets sidecar. */\n channelSecrets?: Record<string, string>\n /** Channel used when a request does not name one. */\n defaultChannelId?: string\n /** Model alias selected by default in the GUI. Empty uses the first option. */\n defaultModel?: string\n promptApiUrl?: string\n promptApiKey?: string\n promptModel?: string\n /* ----- deprecated legacy single-endpoint fields (migrated to channels) ----- */\n apiUrl?: string\n apiKey?: string\n imageModels?: string[]\n}\n\n/** One settings path-op as the bridge consumes it. */\nexport type SettingsOp = { op: 'set'; path: string[]; value: unknown } | { op: 'unset'; path: string[] }\n\n/** Wire shape of one namespace view from the bridge. */\ninterface BridgeView {\n ns: string\n value: unknown\n base?: unknown\n user?: unknown\n revision: number\n secrets?: Array<{ path: string[]; set: boolean }>\n}\n\n/** The bridge response envelope ({ ok: true, value } | { ok: false, code, message }). */\ntype BridgeEnvelope =\n | { ok: true; value: { namespaces?: BridgeView[]; writable?: boolean } | BridgeView }\n | { ok: false; code: string; message: string }\n\n/** Settings wire face over the bridge routes (fetch-backed). */\nfunction createBridgeApi(fetchFn: typeof fetch): {\n settings: {\n describe(payload: Record<string, never>): Promise<{ result: BridgeEnvelope }>\n mutate(payload: { ns: string; ops: unknown[]; expectedRevision?: number }): Promise<{ result: BridgeEnvelope }>\n }\n} {\n const post = async (path: string, body: unknown): Promise<{ result: BridgeEnvelope }> => {\n try {\n const response = await fetchFn(path, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n return { result: { ok: false, code: 'internal', message: `bridge HTTP ${response.status}` } }\n }\n return { result: await response.json() as BridgeEnvelope }\n } catch {\n return { result: { ok: false, code: 'internal', message: 'settings bridge unreachable' } }\n }\n }\n return {\n settings: {\n describe: async payload => post(SETTINGS_API.describe, payload),\n mutate: async payload => post(SETTINGS_API.mutate, payload),\n },\n }\n}\n\n/**\n * A SettingsScope over the bridge face: serialized queue, revision-fenced\n * writes, recovery read after a refusal. Mirrors the official controller's\n * ordering but trusts the Host-seam value without re-running the wire-schema\n * validation — the seam already validated it.\n */\nclass BridgeScopeController<T> implements SettingsScope<T> {\n private readonly store: SnapshotStore<SettingsScopeSnapshot<T>>\n /** Whether the namespace currently holds a stored secret (e.g. apiKey). */\n private readonly keySet: SnapshotStore<boolean>\n /** Individual secret presence bits, keyed by the settings field name. */\n private readonly secretSets: SnapshotStore<Record<string, boolean>>\n private tail: Promise<void> = Promise.resolve()\n private disposed = false\n\n constructor(\n private readonly api: ReturnType<typeof createBridgeApi>['settings'],\n private readonly spec: { namespace: string },\n ) {\n this.store = createSnapshotStore<SettingsScopeSnapshot<T>>({\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n })\n this.keySet = createSnapshotStore(false)\n this.secretSets = createSnapshotStore({})\n }\n\n getSnapshot(): SettingsScopeSnapshot<T> {\n return this.store.getSnapshot()\n }\n\n /** Whether a stored secret exists (from the redacted view's secrets list). */\n getKeySetSnapshot(): boolean {\n return this.keySet.getSnapshot()\n }\n\n /** Observe the secret-set flag. */\n subscribeKeySet(listener: () => void): () => void {\n return this.keySet.subscribe(listener)\n }\n\n /** Whether a specific secret field currently has a stored value. */\n getSecretSetSnapshot(field: string): boolean {\n return this.secretSets.getSnapshot()[field] === true\n }\n\n /** Observe changes to individual secret-field presence bits. */\n subscribeSecretSets(listener: () => void): () => void {\n return this.secretSets.subscribe(listener)\n }\n\n subscribe(listener: () => void): () => void {\n return this.store.subscribe(listener)\n }\n\n /** Queue a bridge refresh. */\n load(): Promise<void> {\n return this.enqueue(() => this.read())\n }\n\n set(field: string, value: unknown): Promise<void> {\n return this.enqueue(() => this.writeOps([{ op: 'set', path: [field], value }]))\n }\n\n unset(field: string): Promise<void> {\n return this.enqueue(() => this.writeOps([{ op: 'unset', path: [field] }]))\n }\n\n mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<void> {\n return this.enqueue(() => this.writeOps([...ops] as SettingsOp[], expectedRevision))\n }\n\n /** Apply several path ops in one revision-fenced mutate call (atomic save).\n * Path ops may address plain-object fields (e.g. `channelSecrets.<id>`),\n * but never navigate *inside* arrays — write array fields wholesale. */\n mutateOps(ops: SettingsOp[]): Promise<void> {\n return this.enqueue(() => this.writeOps(ops))\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n await this.tail\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n if (this.disposed) return Promise.resolve()\n const task = this.tail.then(async () => {\n if (this.disposed) return\n await operation()\n })\n this.tail = task.catch(() => {})\n return task\n }\n\n private async read(): Promise<void> {\n let response\n try {\n response = await this.api.describe({})\n } catch {\n if (!this.disposed) this.store.update(draft => { draft.status = 'unavailable' })\n return\n }\n if (!response.result.ok || this.disposed) {\n if (!this.disposed) this.store.update(draft => { draft.status = 'unavailable' })\n return\n }\n const { namespaces, writable } = response.result.value as { namespaces?: BridgeView[]; writable?: boolean }\n const view = namespaces?.find(candidate => candidate.ns === this.spec.namespace)\n if (view === undefined) {\n this.store.update(draft => {\n draft.status = 'unavailable'\n draft.writable = writable === true\n })\n this.keySet.set(false)\n this.secretSets.set({})\n return\n }\n this.accept(view, writable)\n }\n\n private async writeOps(ops: SettingsOp[], expectedRevision?: number): Promise<void> {\n const revision = expectedRevision ?? this.getSnapshot().revision\n let response\n try {\n response = await this.api.mutate({\n ns: this.spec.namespace,\n ops,\n ...revision === undefined ? {} : { expectedRevision: revision },\n })\n } catch {\n await this.read()\n return\n }\n if (!response.result.ok || this.disposed) {\n await this.read()\n return\n }\n this.accept(response.result.value as BridgeView, undefined)\n }\n\n private accept(view: BridgeView, writable: boolean | undefined): void {\n this.store.update(draft => {\n draft.revision = view.revision\n draft.base = view.base\n draft.user = view.user\n if (writable !== undefined) draft.writable = writable\n draft.status = 'ready'\n // Trust the Host-seam value: the seam already validated it, and the\n // card binds without a narrowing decoder.\n draft.value = view.value as T\n })\n const secretSets = Object.fromEntries((view.secrets ?? []).map(secret => [secret.path.join('.'), secret.set]))\n this.keySet.set(Object.values(secretSets).some(Boolean))\n this.secretSets.set(secretSets)\n }\n}\n\n/** The bound scope plus the secret-set flag, as the card and panel consume it. */\nexport interface ImageGenScope extends SettingsScope<ImageGenConfig> {\n /** Queue a bridge refresh (the invalidation path re-reads the namespace). */\n load(): Promise<void>\n /** Apply several path ops in one revision-fenced mutate call. */\n mutateOps(ops: SettingsOp[]): Promise<void>\n getKeySetSnapshot(): boolean\n subscribeKeySet(listener: () => void): () => void\n getSecretSetSnapshot(field: string): boolean\n subscribeSecretSets(listener: () => void): () => void\n}\n\n/**\n * Bind the dsh-imagegen settings scope over the bridge routes and start its\n * initial read (the caller mounts nothing until the scope settles).\n * @param fetchFn - the fetch implementation (the global fetch by default).\n * @returns the scope; unavailable when the bridge is unreachable.\n */\nexport function bindImageGenScope(fetchFn: typeof fetch = fetch): ImageGenScope {\n const controller = new BridgeScopeController<ImageGenConfig>(createBridgeApi(fetchFn).settings, {\n // The host keys a plugin's settings descriptor by its loader entry id,\n // which is the host half's `export const name` — the same value the\n // bundle patch row uses.\n namespace: IMAGEGEN_SETTINGS_NAMESPACE,\n })\n void controller.load()\n return controller\n}\n\n/**\n * Project the configured channels into cascading model-picker groups. The\n * flat `models` list remains for history filters and compatibility, while\n * `groups` preserves which channel owns each alias (aliases may repeat across\n * channels). Falls back to the legacy flat allow-list while no channels exist.\n * Pure projection — no host calls.\n */\nexport function imageModelOptions(config: ImageGenConfig | undefined): {\n models: string[]\n groups: ImageModelGroup[]\n defaultChannelId?: string\n defaultModel?: string\n} {\n const channels = config?.channels ?? []\n if (channels.length === 0) {\n const legacy = Array.isArray(config?.imageModels)\n ? config.imageModels.filter((model): model is string => typeof model === 'string' && model.trim() !== '')\n : []\n const defaultModel = typeof config?.defaultModel === 'string' && legacy.includes(config.defaultModel)\n ? config.defaultModel\n : undefined\n const ordered = defaultModel === undefined ? legacy : [defaultModel, ...legacy.filter(model => model !== defaultModel)]\n return {\n models: ordered,\n groups: ordered.length === 0 ? [] : [{ id: '', name: '', models: ordered }],\n ...defaultModel === undefined ? {} : { defaultModel },\n }\n }\n\n const defaultId = config?.defaultChannelId !== undefined && channels.some(channel => channel.id === config.defaultChannelId)\n ? config.defaultChannelId\n : channels[0]!.id\n const orderedChannels = [\n channels.find(channel => channel.id === defaultId)!,\n ...channels.filter(channel => channel.id !== defaultId),\n ]\n const groups: ImageModelGroup[] = orderedChannels.flatMap(channel => {\n const models = channel.models\n .map(model => model.alias.trim())\n .filter((alias, index, all) => alias !== '' && all.indexOf(alias) === index)\n return models.length === 0 ? [] : [{ id: channel.id, name: channel.name.trim() || channel.id, models }]\n })\n\n const aliases: string[] = []\n for (const group of groups) {\n for (const alias of group.models) {\n if (!aliases.includes(alias)) aliases.push(alias)\n }\n }\n const defaultModel = typeof config?.defaultModel === 'string' && aliases.includes(config.defaultModel)\n ? config.defaultModel\n : undefined\n const models = defaultModel === undefined ? aliases : [defaultModel, ...aliases.filter(model => model !== defaultModel)]\n return {\n models,\n groups,\n defaultChannelId: defaultId,\n ...defaultModel === undefined ? {} : { defaultModel },\n }\n}\n","/** Inline renderer for image-generation tool-result attachments. */\n\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport { useEffect, useMemo, useState } from 'react'\nimport { AGENT_IMAGE_API } from '../protocol.ts'\nimport css from './image-toolview.module.css'\n\n/** Owner props supplied by the host's keyed tool-call slot. */\nexport interface ImageToolViewOwnerProps {\n callId: string\n toolName: string\n block: ToolCallBlock\n cwd?: string\n home?: string\n openFile: (path: string) => void\n inspect?: () => void\n}\n\ninterface LoadedImage {\n ref: ImageAttachmentRef\n src: string\n}\n\ninterface ImageToolViewProps extends ImageToolViewOwnerProps {\n sessionId: SessionId\n}\n\nfunction isSettled(block: ToolCallBlock): block is Extract<ToolCallBlock, { kind: 'tool-result' }> {\n return 'kind' in block\n}\n\nfunction imageRefsOf(block: ToolCallBlock): ImageAttachmentRef[] {\n if (!isSettled(block)) return []\n return block.content\n .flatMap(content => content.type === 'image' ? [content.attachment] : [])\n}\n\nfunction textOf(block: ToolCallBlock): string {\n if (!isSettled(block)) return ''\n return block.content\n .filter(content => content.type === 'text')\n .map(content => content.text)\n .join('\\n')\n}\n\nfunction resultInfo(block: ToolCallBlock): { status: string; message: string } {\n if (!isSettled(block)) return { status: 'running', message: '正在生成图片…' }\n const text = textOf(block)\n try {\n const parsed = JSON.parse(text) as { status?: unknown; message?: unknown }\n return {\n status: typeof parsed.status === 'string' ? parsed.status : block.isError ? 'failed' : 'completed',\n message: typeof parsed.message === 'string' ? parsed.message : '',\n }\n } catch {\n return { status: block.isError ? 'failed' : 'completed', message: text }\n }\n}\n\nfunction statusLabel(status: string): string {\n if (status === 'running' || status === 'queued') return '生成中'\n if (status === 'failed') return '生成失败'\n if (status === 'cancelled') return '已取消'\n return '图片结果'\n}\n\nfunction useAttachmentImages(\n sessionId: SessionId,\n refs: ImageAttachmentRef[],\n load: (sessionId: SessionId, ref: ImageAttachmentRef) => Promise<string>,\n): { images: LoadedImage[]; error: string | null } {\n const key = useMemo(() => refs.map(ref => String(ref.attachmentId)).join('|'), [refs])\n const [images, setImages] = useState<LoadedImage[]>([])\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let disposed = false\n const urls: string[] = []\n const revoke = (): void => {\n for (const url of urls) URL.revokeObjectURL(url)\n urls.length = 0\n }\n\n setImages([])\n setError(null)\n if (refs.length === 0) return () => { /* no attachments to clean up */ }\n\n void Promise.all(refs.map(async ref => {\n const src = await load(sessionId, ref)\n urls.push(src)\n return { ref, src }\n }))\n .then(next => {\n if (!disposed) setImages(next)\n })\n .catch(errorValue => {\n revoke()\n if (!disposed) setError(errorValue instanceof Error ? errorValue.message : String(errorValue))\n })\n\n return () => {\n disposed = true\n revoke()\n }\n }, [key, load, refs, sessionId])\n\n return { images, error }\n}\n\n/** Register the inline image result view for all image-generation result tools. */\nexport function registerImageToolviews(ctx: ClientContext): void {\n const load = async (_sessionId: SessionId, ref: ImageAttachmentRef): Promise<string> => {\n // Tool-result images intentionally do not occur in model-visible session\n // content, so session.readAttachment() rejects them. The plugin route\n // reads the same durable attachment after validating the complete ref.\n const query = new URLSearchParams({\n attachment_id: String(ref.attachmentId),\n media_type: ref.mediaType,\n bytes: String(ref.bytes),\n width: String(ref.width),\n height: String(ref.height),\n })\n const response = await fetch(`${AGENT_IMAGE_API}?${query.toString()}`)\n if (!response.ok) throw new Error(`无法读取图片附件(HTTP ${response.status})。`)\n const blob = await response.blob()\n return URL.createObjectURL(blob)\n }\n\n const ImageToolView = (props: ImageToolViewProps): React.JSX.Element => {\n const refs = useMemo(() => imageRefsOf(props.block), [props.block])\n const { status, message } = resultInfo(props.block)\n const { images, error } = useAttachmentImages(props.sessionId, refs, load)\n\n return <section className={css.root} data-state={status} data-tool={props.toolName}>\n <header className={css.header}>\n <span className={css.icon} aria-hidden=\"true\">▧</span>\n <strong>{props.toolName}</strong>\n <span className={css.status}>{statusLabel(status)}</span>\n </header>\n {message !== '' && <p className={css.message}>{message}</p>}\n {images.length > 0 && <div className={css.images}>\n {images.map(image => <a\n className={css.imageLink}\n href={image.src}\n key={String(image.ref.attachmentId)}\n rel=\"noreferrer\"\n target=\"_blank\"\n title=\"打开原图\"\n >\n <img className={css.image} src={image.src} alt={image.ref.name ?? '生成图片'} />\n </a>)}\n </div>}\n {refs.length > 0 && images.length === 0 && error === null && <p className={css.loading}>正在加载图片…</p>}\n {error !== null && <p className={css.error}>{error}</p>}\n </section>\n }\n\n ctx.slots.inject('tool.call.toolview', function* () {\n for (const key of ['generate_image', 'edit_image', 'get_image_generation_task']) {\n yield ctx.slots.register({\n name: 'tool.call.toolview',\n key,\n inject: (sessionId: string) => ({ sessionId: sessionId as SessionId }),\n }, ImageToolView)\n }\n })\n}\n","/**\n * Browser-half entry for the dsh-imagegen plugin — runs inside the dsh web GUI.\n *\n * Registers the locale dictionaries, binds the plugin's own settings scope (its\n * bridge routes serve the namespace), registers the row's configuration entry\n * onto the Plugins page's per-row configuration slot, and registers the inline\n * image renderer for image-generation tool results. Failure policy: mounting\n * problems are logged, never thrown — the web shell fails the whole boot when a\n * plugin apply throws, and an external plugin must not take the GUI down.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\n// Type-only: pulls the connection Context merge (ctx.connection, connection/reset).\nimport type {} from '@deepseek-ai/dsh-client-connection/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: pulls the LocaleNamespaceMap merge table.\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { applyHostLocale } from './helpers.ts'\nimport { en, ru, zh, type ImageGenKey } from './locales.ts'\nimport { ImageGenSettingsCard, ImageGenSettingsCardController } from './SettingsCard.tsx'\nimport { bindImageGenScope, type ImageGenScope } from './settings-scope.ts'\nimport { registerImageToolviews, type ImageToolViewOwnerProps } from './image-toolview.tsx'\n\n/** Locale namespace this plugin owns. */\nconst NS = 'dsh-imagegen'\n\n/**\n * The patch row this plugin's own `cordis.patch.yml` inserts.\n *\n * It equals the host half's `export const name` in `src/index.ts`, and the\n * browser half addresses its configuration entry by\n * `<bundle package name>#<this id>`.\n */\nexport const IMAGEGEN_ROW_ID = 'imagegen'\n\n/**\n * Bundle package names whose row {@link IMAGEGEN_ROW_ID} this card configures.\n *\n * The Plugins page keys a row's configuration by the name of the package that\n * declares the row. This plugin reaches a profile in one of two shapes, and the\n * key differs between them: as a dependency of this repository's aggregate\n * bundle, the declaring package is the aggregate; installed on its own, it is\n * this package. Both keys are registered — the one whose bundle is not\n * installed simply never renders, because the page dispatches only the keys its\n * own bundles declare.\n */\nexport const IMAGEGEN_BUNDLE_NAMES = [\n '@logictan/dsh-plugins-all',\n '@logictan/dsh-imagegen',\n] as const\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-imagegen surface copy. */\n 'dsh-imagegen': ImageGenKey\n }\n\n interface SlotMap {\n /**\n * The official plugin row-configuration slot the Plugins page declares for\n * a row a bundle contributes (the configure control beside the row opens\n * this entry's own page). This card registers there as its own standalone\n * entry — independent of the dsh-web-ui family group — so this plugin never\n * reads as part of that family. Spelled here with the same shape so this\n * package can register without depending on the sibling UI package.\n */\n 'plugins.row.config': { kind: 'keyed'; scope: 'root'; owner: ImageGenPluginConfigOwnerProps }\n /** Image-generation results render their durable image blocks inline. */\n 'tool.call.toolview': { kind: 'keyed'; scope: 'session'; owner: ImageToolViewOwnerProps }\n }\n}\n\n/** Owner share of a plugin row's configuration entry. */\nexport interface ImageGenPluginConfigOwnerProps {\n /** The view the Plugins page asks for: the row's one-liner, or its page. */\n readonly view: 'summary' | 'page'\n}\n\n/** Required services (fiber inject waiting — the runtime must be up first). */\nexport const inject = ['slots', 'locale', 'connection']\n\n/**\n * Mount the settings card and the inline image renderer.\n * @param ctx - client root context (services: slots, locale, connection).\n */\nexport function apply(ctx: ClientContext): void {\n // The host locale service only knows zh/en dictionaries (its type is\n // fixed); ru rides the untyped single-locale registration instead.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-imagegen: dictionaries')\n // Russian ships as a language pack: the dictionary lands in this plugin's\n // namespace, and Русский joins the shared DSH language catalog (Settings →\n // General → Language) with per-key fallback to English for host copy.\n // Registration order/aggregation between register(NS, 'ru', …) and\n // addLanguage differs across host builds and a duplicate throws — these\n // surfaces must degrade silently, never fail the GUI boot.\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, 'ru', ru)\n } catch (error) {\n console.warn('[dsh-imagegen] ru dictionary not registered:', error)\n return () => {}\n }\n }, 'dsh-imagegen: ru dictionary')\n ctx.effect(() => {\n try {\n if (ctx.locale.getLocale().locales.some(locale => locale.id === 'ru')) return () => {}\n return ctx.locale.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })\n } catch (error) {\n console.warn('[dsh-imagegen] ru language not added to the catalog:', error)\n return () => {}\n }\n }, 'dsh-imagegen: ru language pack')\n // Every plugin surface renders through tt(); bridge DSH locale switches\n // into it so the whole plugin follows the interface language.\n ctx.effect(() => {\n const applyLocale = (): void => { applyHostLocale(ctx.locale.getLocale().active) }\n applyLocale()\n return ctx.locale.subscribe(applyLocale)\n }, 'dsh-imagegen: follow host locale')\n registerImageToolviews(ctx)\n\n // The bridge routes answer to the composition's trust fence, so every browser\n // the deployment admits can read them — including one on a declared LAN\n // authority. A page the fence refuses gets `unavailable` from the scope's own\n // failure path, which is the same state this used to hard-code for remote\n // browsers; the gate that did so would now deny exactly the browsers the fence\n // admits.\n const scope: ImageGenScope = bindImageGenScope()\n\n // Re-read the scope whenever the connection resets (same invalidation the\n // official settings binder wires).\n ctx.effect(() => {\n const disposers = [\n ctx.on('connection/reset', () => { void scope.load() }),\n ]\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-imagegen: settings scope invalidation')\n\n // Plugin configuration entry: one staged form over the plugin's settings\n // scope, registered onto the Plugins page's per-row configuration slot. One\n // entry per bundle that can declare this row; the slot is key-dispatched by\n // `<bundle>#<row id>`, so both keys are registered and the bundle that is not\n // installed simply never dispatches its key.\n const settingsCard = new ImageGenSettingsCardController(scope)\n for (const bundle of IMAGEGEN_BUNDLE_NAMES) {\n ctx.slots.inject('plugins.row.config', () => ctx.slots.register({\n name: 'plugins.row.config',\n key: `${bundle}#${IMAGEGEN_ROW_ID}`,\n locale: NS,\n inject: () => settingsCard.inject(),\n }, ImageGenSettingsCard))\n }\n}\n"],"mappings":"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"}