Skip to content

Commit 6159202

Browse files
committed
- Web editor.
1 parent 3a97625 commit 6159202

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+8200
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Deploy Web Editor (GitHub Pages)
2+
3+
on:
4+
push:
5+
branches: ["master"]
6+
paths:
7+
- "web-editor/**"
8+
- ".github/workflows/pages-web-editor.yml"
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: "pages"
18+
cancel-in-progress: true
19+
20+
jobs:
21+
build:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v4
26+
27+
- name: Setup Node
28+
uses: actions/setup-node@v4
29+
with:
30+
node-version: 20
31+
cache: npm
32+
cache-dependency-path: web-editor/package-lock.json
33+
34+
- name: Install
35+
working-directory: web-editor
36+
run: npm ci
37+
38+
- name: Build
39+
working-directory: web-editor
40+
run: npm run build
41+
42+
- name: Upload artifact
43+
uses: actions/upload-pages-artifact@v3
44+
with:
45+
path: web-editor/dist
46+
47+
deploy:
48+
needs: build
49+
runs-on: ubuntu-latest
50+
environment:
51+
name: github-pages
52+
url: ${{ steps.deployment.outputs.page_url }}
53+
steps:
54+
- name: Deploy
55+
id: deployment
56+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ logs
2828
.kotlin
2929

3030
# VS Code
31-
*.code-workspace
31+
*.code-workspace
32+
33+
# web-editor (Node/Vite)
34+
web-editor/node_modules
35+
web-editor/dist
36+
web-editor/.vite
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package integration.jei
2+
3+
import github.kasuminova.prototypemachinery.integration.jei.builtin.PMJeiIcons
4+
import org.junit.jupiter.api.Assertions.assertNotNull
5+
import org.junit.jupiter.api.Test
6+
7+
class JeiRecipeIconsExistenceTest {
8+
9+
@Test
10+
fun `all referenced jei recipeicons exist on classpath`() {
11+
// Background
12+
assertRecipeIconExists("jei_base.png")
13+
14+
// Item module
15+
assertRecipeIconExists("item_module/plaid_base.png")
16+
17+
// Plaid module (used by ItemRequirementJeiRenderer)
18+
listOf(
19+
"plaid_module/base_top_0101.png",
20+
"plaid_module/base_top_0111.png",
21+
"plaid_module/base_top_0110.png",
22+
"plaid_module/base_mid_1101.png",
23+
"plaid_module/base_mid_1111.png",
24+
"plaid_module/base_mid_1110.png",
25+
"plaid_module/base_down_1001.png",
26+
"plaid_module/base_down_1011.png",
27+
"plaid_module/base_down_1010.png",
28+
29+
"plaid_module/0000.png",
30+
"plaid_module/1010.png",
31+
"plaid_module/1000.png",
32+
"plaid_module/1001.png",
33+
"plaid_module/0001.png",
34+
"plaid_module/0010.png",
35+
"plaid_module/0110.png",
36+
"plaid_module/0100.png",
37+
"plaid_module/0101.png",
38+
).forEach(::assertRecipeIconExists)
39+
40+
// Fluid module variants (used by FluidRequirementJeiRenderer)
41+
PMJeiIcons.ALL_VARIANTS.values
42+
.filter { it.id.path.startsWith("fluid/") }
43+
.forEach { variant ->
44+
val key = when (variant.id) {
45+
PMJeiIcons.FLUID_PLAID_1X1 -> "1_1_plaid"
46+
else -> variant.id.path.removePrefix("fluid/")
47+
.replace("x", "_")
48+
}
49+
50+
// 0o5_1 is currently shipped under gas_module/.
51+
val moduleDir = if (key == "0o5_1") "gas_module" else "fluid_module"
52+
53+
assertRecipeIconExists("$moduleDir/${key}_base.png")
54+
assertRecipeIconExists("$moduleDir/${key}_top.png")
55+
}
56+
57+
// Energy module variants (used by EnergyRequirementJeiRenderer)
58+
PMJeiIcons.ALL_VARIANTS.values
59+
.filter { it.id.path.startsWith("energy/") }
60+
.forEach { variant ->
61+
val suffix = variant.id.path.removePrefix("energy/")
62+
val key = when (suffix) {
63+
"default" -> "default_in"
64+
else -> suffix.replace("x", "_")
65+
}
66+
67+
assertRecipeIconExists("energy_module/${key}_empty.png")
68+
assertRecipeIconExists("energy_module/${key}_full.png")
69+
}
70+
71+
// Progress module textures (used by ProgressModuleJeiDecorator)
72+
listOf(
73+
// Simple base/run pairs
74+
"right",
75+
"left",
76+
"up",
77+
"down",
78+
"compress",
79+
"cut",
80+
"merge",
81+
"split",
82+
"rolling",
83+
).forEach { type ->
84+
assertRecipeIconExists("progress_module/${type}_base.png")
85+
assertRecipeIconExists("progress_module/${type}_run.png")
86+
}
87+
88+
// Heat/Cool special cases
89+
assertRecipeIconExists("progress_module/heat_base.png")
90+
assertRecipeIconExists("progress_module/heat_run_0.png")
91+
assertRecipeIconExists("progress_module/heat_run_1.png")
92+
}
93+
94+
private fun assertRecipeIconExists(relPath: String) {
95+
val fullPath = "assets/prototypemachinery/textures/gui/jei_recipeicons/$relPath"
96+
val url = javaClass.classLoader.getResource(fullPath)
97+
assertNotNull(url, "Missing recipe icon resource on classpath: $fullPath")
98+
}
99+
}

web-editor/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# PrototypeMachinery Web Editor (Draft)
2+
3+
一个独立的 Web 子项目(`web-editor/`),用于可视化编辑 JEI/UI/配方相关数据,并导出 ZenScript 脚本。
4+
5+
本项目的优先目标:**可维护、可演进、先把闭环跑通**。我们会刻意避免“为了架构而架构”,用最少的抽象换取长期可控的复杂度。
6+
7+
## 技术栈
8+
9+
- Vite + React + TypeScript
10+
- Mantine(组件库,快速做出“像样”的界面)
11+
- react-konva/Konva(画布拖拽与元素编辑)
12+
- Zustand(状态管理)
13+
- Zod(IR schema 校验)
14+
15+
## 运行
16+
17+
`web-editor/` 目录:
18+
19+
如果你的系统只有 `node` 但没有 `npm`(当前工作区就是这种情况),可以用 pacman 安装:
20+
21+
- `sudo pacman -S --needed npm`
22+
23+
- `npm install`
24+
- `npm run dev`
25+
26+
其他常用命令:
27+
28+
- `npm run build`:产出 `dist/`(用于 Pages)
29+
- `npm run typecheck`:严格类型检查
30+
- `npm run lint`:代码规范检查
31+
32+
## 部署
33+
34+
仓库根目录提供 GitHub Pages workflow,会构建 `web-editor/` 并部署到 Pages。
35+
36+
> 注意:我们使用 HashRouter + Vite `base: './'`,适配子路径静态托管。
37+
38+
## 要做的活(Roadmap)与预期效果
39+
40+
### 基层框架(Foundation,当前优先)
41+
42+
这些是“以后所有功能都依赖它”的底座能力:
43+
44+
1) **可撤销/重做(Undo/Redo)**
45+
- 预期效果:拖拽、属性修改、增删元素、导入等操作都能 `Ctrl+Z / Ctrl+Y` 回退/重做。
46+
- 设计约束:避免在拖拽过程中产生海量历史(仅在 `dragEnd` 记录一次)。
47+
48+
2) **基础快捷键**
49+
- 预期效果:
50+
- `Ctrl+Z` 撤销、`Ctrl+Shift+Z`/`Ctrl+Y` 重做
51+
- `Delete` 删除选中元素
52+
53+
3) **画布尺寸可编辑**
54+
- 预期效果:右侧面板可改 canvas 的 `width/height`,即时反映到画布。
55+
56+
4) **稳定的最小 IR(中间格式)**
57+
- 预期效果:导出 JSON 能被校验(Zod),后续加字段不会破坏旧数据。
58+
- 原则:IR 只表达“布局意图”和“渲染所需参数”,不要在 IR 内塞运行时逻辑。
59+
60+
### 迭代增强(按需推进,避免过度设计)
61+
62+
1) **元素预设与更友好的添加方式**
63+
- 预期效果:添加 `slot/tank/energy/text` 有默认尺寸与默认 data。
64+
65+
2) **视图能力:缩放/平移/对齐线**
66+
- 预期效果:画布更像“编辑器”而不是“静态图片”,定位元素更轻松。
67+
- TODO:补充快捷键缩放(例如 `Ctrl +` / `Ctrl -` / `Ctrl 0`)。
68+
- 已实现:滚轮缩放;Space+拖拽平移;Fit-to-View;双击背景 Fit-to-View;定位到选中元素。
69+
70+
3) **资产系统(最简版)**
71+
- 预期效果:内建素材索引 + 用户导入图片(本地)+ 简单参数校验(如 9-slice)。
72+
73+
4) **导出对接真实脚本语义**
74+
- 预期效果:ZenScript 导出不再是 stub:
75+
- 有稳定 nodeId/role/variant 语义
76+
- `data` 能映射到 mod 端 placement data(例如 `energyLedYOffset`)
77+
78+
## 目录结构(保持简单)
79+
80+
- `src/pages/`:页面与布局(UI 壳)
81+
- `src/editor/`:编辑器核心
82+
- `model/`:IR schema 与类型
83+
- `store/`:状态与操作(含 undo/redo)
84+
- `components/`:画布/编辑器组件
85+
- `io/`:本地存储、示例、下载
86+
- `exporters/`:导出器(ZenScript/JSON)

web-editor/eslint.config.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import js from '@eslint/js';
2+
import tseslint from '@typescript-eslint/eslint-plugin';
3+
import tsParser from '@typescript-eslint/parser';
4+
import reactHooks from 'eslint-plugin-react-hooks';
5+
import reactRefresh from 'eslint-plugin-react-refresh';
6+
7+
export default [
8+
{
9+
// 不对构建产物/依赖做 lint(否则会出现成百上千的误报)
10+
ignores: ['dist/**', 'node_modules/**'],
11+
},
12+
js.configs.recommended,
13+
{
14+
files: ['**/*.{ts,tsx}'],
15+
languageOptions: {
16+
parser: tsParser,
17+
parserOptions: {
18+
ecmaVersion: 'latest',
19+
sourceType: 'module',
20+
},
21+
},
22+
plugins: {
23+
'@typescript-eslint': tseslint,
24+
'react-hooks': reactHooks,
25+
'react-refresh': reactRefresh,
26+
},
27+
rules: {
28+
// TS 会自行检查未声明标识符;eslint 的 no-undef 会对 DOM 全局/TS 类型名产生误报。
29+
'no-undef': 'off',
30+
31+
// 用 TS 版本替代基础规则,避免对 type-only 参数/泛型等误判。
32+
'no-unused-vars': 'off',
33+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
34+
35+
...reactHooks.configs.recommended.rules,
36+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
37+
},
38+
},
39+
];

web-editor/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>PrototypeMachinery Editor</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)