Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fa1a563
feat(boxes): add the box resource contract and annotation visibility
AliceJump Sep 28, 2026
c82b6b0
fix(annotations): select a box from its name and nudge it with arrows
AliceJump Sep 28, 2026
c6df741
feat(boxes): add box asset and gallery views
AliceJump Sep 28, 2026
dcdbaed
fix(boxes): keep authoring files intact and fix editor completion
AliceJump Sep 28, 2026
1751d4f
fix(boxes): do not overwrite a box file that could not be read
AliceJump Sep 28, 2026
d18b5e2
fix(boxes): refuse a publish that would overwrite the authoring file
AliceJump Sep 28, 2026
c213b01
chore(jetbrains): point the submodule at the conflict resolution
AliceJump Sep 28, 2026
cf2bc18
chore(jetbrains): point the submodule at the locale fix
AliceJump Sep 28, 2026
ce2aad8
fix(boxes): refuse a rectangle that the next read would reject
AliceJump Sep 28, 2026
7f1037f
chore(jetbrains): point the submodule at the unsaved path fix
AliceJump Sep 28, 2026
4c0fd04
fix(boxes): keep box and annotation management on the same page
AliceJump Sep 29, 2026
5a5239c
fix(boxes): remove box records before deleting the image file
AliceJump Sep 29, 2026
86a490c
fix(boxes): do not treat an unreadable boxes.json as an empty catalog
AliceJump Sep 29, 2026
137aafd
chore(jetbrains): point the submodule at the filename-matching test
AliceJump Sep 29, 2026
b5ba3db
fix(boxes): put box records back when the image file stays
AliceJump Sep 29, 2026
13fa13a
fix(boxes): put the image back when saving annotations fails
AliceJump Sep 29, 2026
bcfffcd
chore(jetbrains): point the submodule at the delete exception restore
AliceJump Sep 29, 2026
e964b24
fix(boxes): keep image deletion from losing a case-different record
AliceJump Sep 29, 2026
2cff566
chore(jetbrains): point the submodule at the merged box resource PR (…
AliceJump Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions docs/box-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# 框资源设计

框管理对标现有模板管理,分成两份资源。标注工作副本只给插件编辑。运行时副本是发布后的位置表:插件的框管理和 `self.pos` 补全读它。游戏进程要等业务项目的 `ScreenPosition` 加载这份文件之后才会用到它;ok 框架现在的模板匹配仍然只读 `template_matching.coco_feature_json`。图片、画布和显隐不另起一套。

本文是实现依据。ok-neverness-to-everness(下称 ok-nte)里的真实调用是 `self.pos.screen.main_viewport.to_box()`,不是 `self.pos.main_viewport`,也没有 `screen_pos`。

## 1. 现有能力

工具箱有两条已经分开的模板线。

| | 标注资源 | 运行时资源 |
|---|---|---|
| 文件 | `<templates.directory>/coco_annotations.json`,默认目录 `ok_templates` | `config.py` 的 `template_matching.coco_feature_json`,缺省探测 `assets/coco_annotations.json` 与 `ok_tasks/assets/coco_annotations.json` |
| 谁读写 | 标注管理(素材面板) | 模板画廊、补全、Hover;ok 框架做模板匹配 |
| 如何过去 | 显式「导出到 assets」。编辑不会自动写运行时文件 | |

标注编辑器(`AnnotationDialog` / `media/annotationPanel`)已有选择、拖动、八向缩放、增删、缩放、平移、撤销、复制粘贴和跨图导航。内存模型是像素 `xywh`。落盘是 COCO `bbox`。`NormalizedBox` 只服务坐标复制,不落盘,也不是 ok-script 的 `Box`。

编辑器此前没有标注列表,也没有显隐。隐藏和删除是同一件事。

模板画廊只读运行时 COCO。单击插入 `fL.<名称>`。区域预览是原图加像素 bbox 的动态裁剪,缓存键是图片内容哈希加 bbox。Quick Documentation 和 Hover 使用同一张裁剪图。JetBrains 查找项右侧是尺寸;VS Code 在补全项 resolve 时把图放进 documentation。

本仓库没有业务上的 Point 资源,也没有位置表。

## 2. ok-nte 的 pos / Box

位置表在 `src/scene/`,手写 Python。

- `self.pos` 是 `PositionMap`,在 `BaseNTETask` 里创建。
- `self.pos.screen` 是矩形。目前有 `center`、`dialog_icon`、`main_viewport`,四个数是相对整屏的 `(left, top, right, bottom)`。
- `self.pos.panels.*` 是点击点,两个数,用 `*self.pos.panels.esc.mail` 解包。两点调用 `to_box()` 会抛错。
- `ScreenRatio.to_box()` 调用 `box_of_screen(..., hcenter=True)`,按当前采集帧换成像素 `ok.Box`。`hcenter` 发生在调用时,不能写进资源。
- `Box.name` 由描述符写成 `ScreenPosition.<字段>`,只用于调试。查找区域不按这个名字匹配。
- 约 73 处任务私有 `box_of_screen(...)` 明确不进位置表。框资源不收编它们。
- 运行时不依赖 IDE 插件。

## 3. 两份框资源

| | 模板 | 框 |
|---|---|---|
| 标注资源 | `<模板目录>/coco_annotations.json` | `<模板目录>/boxes.json` |
| 运行时资源 | `templates.cocoAnnotations` → `config.py` → 探测 | `boxes.runtime` → `config.py` 的 `boxes_json` → 探测 `src/scene/boxes.json` |
| 编辑入口 | 标注管理 | 框资源管理 |
| 浏览 / 补全 | 模板管理 | 框管理 |
| 从标注到运行时 | 显式导出 | 显式发布 |

游戏加载器不读 `ok-script-toolkit.json`。插件索引运行时文件时,约定优先于 `config.py`,与模板相同。ok-nte 两处都不写时,两边都用 `src/scene/boxes.json`。

`panels` 前缀留给现有点击点。框路径不得占用。

## 4. 数据模型

标注资源带着原图,供编辑和裁剪:

```json
{
"version": 1,
"boxes": [
{
"path": "screen.main_viewport",
"image": "12.png",
"rect": [0.098400, 0.104200, 0.896100, 0.894400]
}
]
}
```

运行时资源只有几何。`to_box()` 不需要图片:

```json
{
"version": 1,
"boxes": [
{
"path": "screen.main_viewport",
"rect": [0.098400, 0.104200, 0.896100, 0.894400]
}
]
}
```

约定:

- `path` 是 `self.pos.` 后面的属性路径。至少两段,每段是 Python 标识符。`screen.main_viewport` 对应 `self.pos.screen.main_viewport`。
- `rect` 是 `[left, top, right, bottom]`,相对这张原图的整幅,范围 0–1。这就是 `ScreenRatio` 的四个数。
- 序列化按 `path` 排序,矩形固定 6 位小数,两空格缩进,文件末尾换行。改一个框时 diff 停在那个对象。
- `image` 是模板目录下的文件名,用现有文件名归一化规则对齐。不复制图片,不把裁剪 PNG 写进仓库。
- 参照图必须是整屏截图。`assets/images` 里的打包裁切块不能当框的原图。
- `to_box()` 的调试名由 path 推导:`screen` 下的叶子写成 `ScreenPosition.<叶子>`。它不是主键。

发布把标注条目投影成 `{path, rect}`,去掉 `image`。发布是整份快照:标注里删掉的框,发布后从运行时消失;若会删掉运行时已有 path,先确认。编辑器保存只写标注资源。

## 5. 图片与编辑器

框引用标注管理正在用的同一批原图。框资源管理不导入图片、不截图入库、不打包导出。

不新写画布。编辑器会话只认识图片和 `id + 标签 + 像素 xywh`。模板适配器的标签是分类名,保存仍写 COCO。框适配器的标签是 path,打开时把 `rect` 换成像素框,写回时:

- 像素矩形没变的条目保留原来的浮点 `rect`。
- 变过的条目才用 `left = x / 宽` 重新计算。

这样打开后直接保存不会改文件。

点不单独做编辑器。条目按 id 工作,以后的点走同一套会话和显隐。

## 6. 显隐

显隐是编辑器会话状态,按条目标记记在隐藏集合里。不进 COCO,不进两份框文件,不进撤销栈。

标注列表始终列出全部条目。画布和命中测试只使用未隐藏的条目。

| 操作 | 行为 |
|---|---|
| 显示全部 | 清空隐藏集合 |
| 隐藏全部 | 隐藏当前图上每一条 |
| 只显示当前 | 只留选中项 |
| 勾选 | 逐条改隐藏集合 |

每张图一份隐藏集合,留在本次编辑会话里。关掉对话框就丢。新图默认全部显示。模板、框、点共用这一个过滤。

## 7. 预览、补全、生成

框管理和 `self.pos.screen.` 的补全只索引运行时资源。插入文本是 `self.pos.screen.main_viewport.to_box()`。另给一个只复制属性路径的动作。

预览图不进运行时文件。用 path 回查标注资源拿 `image`,再按原图和 `rect` 裁剪,复用模板的裁剪缓存。缓存键仍然是内容哈希加像素 bbox,框一改就自然失效。对不上原图时,文档只显示 path 和 rect。

文档内容沿用模板:裁剪图、表达式、path、归一化 rect、原图相对路径。JetBrains 可以加一行短坐标 inlay;VS Code 不加,与「模板不做幽灵注释」一致。

单个模板生成框:用该标注所在原图把像素 `xywh` 归一化,默认 path 建议 `screen.<分类名>`,已存在则要求换名。多个模板生成包围框只允许同一张原图:

```text
left = min(x) / 宽
top = min(y) / 高
right = max(x + w) / 宽
bottom = max(y + h) / 高
```

生成结果是普通框,进入标注资源,发布后才进入运行时。

## 8. 运行时加载

加载放在业务项目的 `ScreenPosition`,不放进插件,也不改 `ok.Box`。

`ScreenRatio` 只有 `__get__`。实例上的同名属性会盖住类描述符。JSON 里有的名字用 JSON;没有的名字仍走手写类属性。`to_box()` 继续调用 `box_of_screen(..., hcenter=True)`。

现有三个矩形可以一次性导入标注资源并绑定整屏原图。发布前手写属性继续有效,插件的框管理和补全只认运行时 JSON。

## 9. 模块

| 位置 | 职责 |
|---|---|
| `src/boxResourcePure.ts` 与 `core/BoxResource.kt` | 路径、矩形、发布、显隐。两端语义一致 |
| `core/BoxRuntimePath.kt` 与纯模块中的路径函数 | 运行时文件取值链,对标 `CocoFeaturePath` |
| `schemas/ok-script-toolkit.schema.json` 的 `boxes.runtime` | 约定文件。没有个人偏好层 |
| `python/probe_window_config.py` 的 `boxes_json` | 从 `config.py` 顶层读出运行时路径 |
| 标注编辑器两侧 | 标注列表和显隐 |
| 后续:框资源管理、框管理、补全、从模板生成 | 按第 10 节往下接,复用上面的契约 |

## 10. 实施顺序

1. 数据契约、路径解析、编辑器显隐。
2. 框资源管理:按原图浏览,用现有编辑器改标注资源。
3. 发布到运行时文件。
4. 框管理卡片和文档预览。
5. `self.pos` 的补全与 Hover。
6. 单模板生成框、同图多模板包围框。
7. ok-nte 的 `ScreenPosition` 加载器。三个矩形确认无误后,再手工去掉对应类属性。

## 11. 风险

- 编辑器改造不能改变模板标注的保存结果。显隐默认全显,撤销不含显隐。
- 打开即保存不得改写未移动框的浮点。
- 框若标在裁切块上,归一化结果不是屏幕比例。
- JSON 与类属性同名时以 JSON 为准。导入并发布后,应在业务项目里删掉对应手写属性,避免两处各改各的。
- 插件不得把 `hcenter` 算进 `rect`。
4 changes: 4 additions & 0 deletions docs/ok-script-toolkit.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"cocoAnnotations": "assets/coco_annotations.json"
},

"boxes": {
"runtime": "src/scene/boxes.json"
},

"i18n": {
"enabled": true,
"langDirectory": "assets/lang",
Expand Down
11 changes: 7 additions & 4 deletions docs/project-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,26 @@
| | `afterConfigImport` | **无此信息** | 按约定试 `src.patches.startup_patches:install_startup_patches` |
| `templates` | `directory` | 无(插件侧约定) | IDE 设置 → `ok_templates` |
| | `cocoAnnotations` | **6/6 有** | `config.py` 的 `template_matching.coco_feature_json` → 依次探测两个候选 |
| `boxes` | `runtime` | 新字段,现有项目还没有 | `config.py` 顶层 `boxes_json` → `src/scene/boxes.json` |
| `i18n` | `enabled` / `langDirectory` / `poDirectory` / `poDomains` | 无 | IDE 设置 → 内置默认 |
| `characters` | `projectPath` / `masterFile` / `skillsDirectory` / `localeFile` / `avatarTemplateRegex` | 无 | IDE 设置 → 内置默认 |
| `effects` | `file` | 无 | IDE 设置 → `src/data/effects.py` |

**接线状态**:全部字段已接线(`templates.directory` / `templates.cocoAnnotations` /
`labelEnum.*` / `i18n` / `characters` / `effects`)。
`boxes.runtime` / `labelEnum.*` / `i18n` / `characters` / `effects`)。
`boxes.runtime` 的后半段(读 `config.py`、定位文件)已接上;框管理界面仍按 `docs/box-resources.md` 继续。

**⚠️ 两个同名的 `coco_annotations.json` 不是一回事** —— 接错会静默指向错的文件:

| 文件 | 是什么 | 谁读写 | 路径由谁决定 |
|---|---|---|---|
| `assets/coco_annotations.json`(或 config.py 指的别处) | ok 框架加载的**运行时模板库** | `featureData` / `OkProjectDataService` 读,文件监听盯它 | `templates.cocoAnnotations` → config.py → 两个惯例位置 |
| `<模板目录>/coco_annotations.json` | 素材面板自己的**标注工作文件** | `templateAssetData` / `TemplateAssetDataService` 读写 | `templates.directory`(**不受** `cocoAnnotations` 影响) |
| `src/scene/boxes.json`(或 `boxes_json` 指的别处) | 业务项目加载的**运行时框** | 路径已能解析;框管理 / 补全尚未读取。加载器在业务项目的 `ScreenPosition` | `boxes.runtime` → config.py → 探测位置 |
| `<模板目录>/boxes.json` | 框资源管理的**标注工作文件** | 契约已定,面板尚未接上 | `templates.directory`(**不受** `boxes.runtime` 影响) |

`cocoAnnotations` 是**唯一**一层"`config.py` 已声明的事实"真正落地的字段 ——
其余字段 `config.py` 要么不声明,要么(`labelEnum.path`)插件至今没读。
它也是唯一**没有 IDE 设置**的链(没有"个人偏好"层),所以不进溯源面板。
`cocoAnnotations` 与 `boxes.runtime` 是"`config.py` 已声明的事实"落地的两条链,
都**没有 IDE 设置**(没有"个人偏好"层)。`boxes_json` 在现有项目里还没有,缺席时探测 `src/scene/boxes.json`。

**按字段类型选归一化方式**(做错是**静默**的,所以这里写死):

Expand Down
2 changes: 1 addition & 1 deletion jetbrains
Submodule jetbrains updated 24 files
+266 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/BoxCatalogService.kt
+335 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/BoxResource.kt
+82 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/BoxRuntimePath.kt
+2 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/OkDataChangeService.kt
+15 −4 src/main/kotlin/com/alicejump/okscripttoolkit/core/OkProjectDataService.kt
+19 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/ProjectConvention.kt
+4 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/ScreenshotCapture.kt
+17 −3 src/main/kotlin/com/alicejump/okscripttoolkit/core/TemplateAssetDataService.kt
+10 −0 src/main/kotlin/com/alicejump/okscripttoolkit/editor/OkCompletionContributor.kt
+95 −2 src/main/kotlin/com/alicejump/okscripttoolkit/editor/OkEditorSupport.kt
+444 −56 src/main/kotlin/com/alicejump/okscripttoolkit/ui/AnnotationDialog.kt
+123 −0 src/main/kotlin/com/alicejump/okscripttoolkit/ui/BoxWindows.kt
+131 −23 src/main/kotlin/com/alicejump/okscripttoolkit/ui/TemplateAssetToolWindowFactory.kt
+20 −0 src/main/resources/META-INF/plugin.xml
+28 −1 src/main/resources/messages/OkScriptToolkitBundle.properties
+28 −1 src/main/resources/messages/OkScriptToolkitBundle_es.properties
+28 −1 src/main/resources/messages/OkScriptToolkitBundle_ja.properties
+28 −1 src/main/resources/messages/OkScriptToolkitBundle_ko.properties
+28 −1 src/main/resources/messages/OkScriptToolkitBundle_zh_CN.properties
+28 −1 src/main/resources/messages/OkScriptToolkitBundle_zh_TW.properties
+183 −0 src/test/kotlin/com/alicejump/okscripttoolkit/core/BoxResourceTest.kt
+9 −0 src/test/kotlin/com/alicejump/okscripttoolkit/core/ProjectConventionTest.kt
+9 −4 src/test/kotlin/com/alicejump/okscripttoolkit/core/TemplateAssetDataServiceCocoTest.kt
+56 −0 src/test/kotlin/com/alicejump/okscripttoolkit/ui/WrappingHintTest.kt
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.es.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"Enter game window title pattern (regex), or leave empty for all windows": "Introduce el patrón de título de la ventana (regex), o déjalo vacío para todas las ventanas",
"Failed to add to template assets: {error}": "No se pudo añadir a recursos de plantillas: {error}",
"Failed to delete: {name}": "No se pudo eliminar: {name}",
"Deleted the record for {name}, but the temporary file is still at {path}.": "Se eliminó el registro de {name}, pero el archivo temporal sigue en {path}.",
"Failed to save screenshot.": "No se pudo guardar la captura.",
"Import images to {dir}": "Importar imágenes a {dir}",
"Imported {count} image(s)": "Se importaron {count} imagen(es)",
Expand Down Expand Up @@ -216,5 +217,8 @@
"Failed to swap annotations.": "No se pudieron intercambiar las anotaciones.",
"Annotations changed while confirming. Retry the swap.": "Las anotaciones cambiaron durante la confirmación. Vuelve a intentar el intercambio.",
"Cannot read image size, so annotations cannot be swapped.": "No se pudo leer el tamaño de la imagen, así que no se pueden intercambiar las anotaciones.",
"Neither image has annotations, nothing to swap.": "Ninguna de las dos imágenes tiene anotaciones, no hay nada que intercambiar."
"Neither image has annotations, nothing to swap.": "Ninguna de las dos imágenes tiene anotaciones, no hay nada que intercambiar.",
"Could not save the box resource.": "No se pudo guardar el recurso de cuadros.",
"Could not save annotations.": "No se pudieron guardar las anotaciones.",
"Publish": "Publicar"
}
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"Enter game window title pattern (regex), or leave empty for all windows": "ゲームウィンドウのタイトル正規表現を入力(空欄ですべてのウィンドウ)",
"Failed to add to template assets: {error}": "テンプレートアセットへの追加に失敗: {error}",
"Failed to delete: {name}": "削除に失敗: {name}",
"Deleted the record for {name}, but the temporary file is still at {path}.": "{name} の記録は削除しましたが、一時ファイルが {path} に残っています。",
"Failed to save screenshot.": "スクリーンショットの保存に失敗しました。",
"Import images to {dir}": "{dir} に画像を読み込む",
"Imported {count} image(s)": "{count} 件の画像を読み込みました",
Expand Down Expand Up @@ -216,5 +217,8 @@
"Failed to swap annotations.": "アノテーションの交換に失敗しました。",
"Annotations changed while confirming. Retry the swap.": "確認中にアノテーションが変更されました。交換をやり直してください。",
"Cannot read image size, so annotations cannot be swapped.": "画像サイズを読み取れないため、アノテーションを交換できません。",
"Neither image has annotations, nothing to swap.": "どちらの画像にもアノテーションがないため、交換するものはありません。"
"Neither image has annotations, nothing to swap.": "どちらの画像にもアノテーションがないため、交換するものはありません。",
"Could not save the box resource.": "ボックス資源を保存できませんでした。",
"Could not save annotations.": "注釈を保存できませんでした。",
"Publish": "公開"
}
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"Enter game window title pattern (regex), or leave empty for all windows": "Enter game window title pattern (regex), or leave empty for all windows",
"Failed to add to template assets: {error}": "Failed to add to template assets: {error}",
"Failed to delete: {name}": "Failed to delete: {name}",
"Deleted the record for {name}, but the temporary file is still at {path}.": "Deleted the record for {name}, but the temporary file is still at {path}.",
"Failed to save screenshot.": "Failed to save screenshot.",
"Import images to {dir}": "Import images to {dir}",
"Imported {count} image(s)": "Imported {count} image(s)",
Expand Down Expand Up @@ -216,5 +217,8 @@
"Failed to swap annotations.": "Failed to swap annotations.",
"Annotations changed while confirming. Retry the swap.": "Annotations changed while confirming. Retry the swap.",
"Cannot read image size, so annotations cannot be swapped.": "Cannot read image size, so annotations cannot be swapped.",
"Neither image has annotations, nothing to swap.": "Neither image has annotations, nothing to swap."
"Neither image has annotations, nothing to swap.": "Neither image has annotations, nothing to swap.",
"Could not save the box resource.": "Could not save the box resource.",
"Could not save annotations.": "Could not save annotations.",
"Publish": "Publish"
}
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"Enter game window title pattern (regex), or leave empty for all windows": "게임 창 제목 정규식을 입력하세요(비우면 모든 창)",
"Failed to add to template assets: {error}": "템플릿 에셋 추가 실패: {error}",
"Failed to delete: {name}": "삭제 실패: {name}",
"Deleted the record for {name}, but the temporary file is still at {path}.": "{name} 기록은 삭제했지만 임시 파일이 {path}에 남아 있습니다.",
"Failed to save screenshot.": "스크린샷 저장에 실패했습니다.",
"Import images to {dir}": "{dir} 로 이미지 가져오기",
"Imported {count} image(s)": "이미지 {count}개를 가져왔습니다",
Expand Down Expand Up @@ -216,5 +217,8 @@
"Failed to swap annotations.": "어노테이션 교환에 실패했습니다.",
"Annotations changed while confirming. Retry the swap.": "확인하는 동안 어노테이션이 변경되었습니다. 교환을 다시 시도하세요.",
"Cannot read image size, so annotations cannot be swapped.": "이미지 크기를 읽을 수 없어 어노테이션을 교환할 수 없습니다.",
"Neither image has annotations, nothing to swap.": "두 이미지 모두 어노테이션이 없어 교환할 것이 없습니다."
"Neither image has annotations, nothing to swap.": "두 이미지 모두 어노테이션이 없어 교환할 것이 없습니다.",
"Could not save the box resource.": "박스 리소스를 저장하지 못했습니다.",
"Could not save annotations.": "주석을 저장하지 못했습니다.",
"Publish": "게시"
}
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"Enter game window title pattern (regex), or leave empty for all windows": "输入游戏窗口标题正则,留空表示匹配所有窗口",
"Failed to add to template assets: {error}": "添加到模板素材失败: {error}",
"Failed to delete: {name}": "删除失败: {name}",
"Deleted the record for {name}, but the temporary file is still at {path}.": "已删除 {name} 的记录,但临时文件还在 {path}。",
"Failed to save screenshot.": "截图保存失败。",
"Import images to {dir}": "导入图片到 {dir}",
"Imported {count} image(s)": "已导入 {count} 张图片",
Expand Down Expand Up @@ -216,5 +217,8 @@
"Failed to swap annotations.": "交换标注失败。",
"Annotations changed while confirming. Retry the swap.": "确认期间标注已更改,请重新交换。",
"Cannot read image size, so annotations cannot be swapped.": "读不出图片尺寸,无法交换标注。",
"Neither image has annotations, nothing to swap.": "两张图都没有标注,无需交换。"
"Neither image has annotations, nothing to swap.": "两张图都没有标注,无需交换。",
"Could not save the box resource.": "无法保存框资源。",
"Could not save annotations.": "无法保存标注。",
"Publish": "发布"
}
Loading
Loading