sapdon 3.3.3 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,409 @@
1
+ # NeoGuidebook API 参考
2
+
3
+ NeoGuidebook 是基于 Minecraft Bedrock JSON UI 体系的指南书生成框架。使用链式 API 声明式构建页面,构建时自动序列化为 JSON UI,运行时通过 ActionFormData 触发显示。
4
+
5
+ ---
6
+
7
+ ## 目录
8
+
9
+ 1. [概览](#1-概览)
10
+ 2. [NeoGuidebook 类](#2-neoguidebook-类)
11
+ 3. [NeoGuidebookPage 类](#3-neoguidebookpage-类)
12
+ 4. [NeoGuidebookBridge 类](#4-neoguidebookbridge-类)
13
+ 5. [ItemComponent 辅助](#5-itemcomponent-辅助)
14
+ 6. [page_ids.json 生成](#6-page_idsjson-生成)
15
+ 7. [运行时注册](#7-运行时注册)
16
+
17
+ ---
18
+
19
+ ## 1. 概览
20
+
21
+ ### 工作流程
22
+
23
+ ```
24
+ 构建时 (main.ts) 运行时 (index.ts)
25
+ ───────────────── ─────────────────
26
+ ItemAPI.createItem() system.beforeEvents.startup
27
+ + setCustomComponentV2() → registerCustomComponent()
28
+ + setMaxStackSize() → onUse(event, params)
29
+ + setDisplayName() → ActionFormData
30
+ + setInteractButton() .title("sapdon_ui:book_name")
31
+ .body("page_id")
32
+ NeoGuidebook("ns:name") → 按钮 .button("prev_button")
33
+ + addDoublePageStack(id, L, R) .show(player)
34
+ + addSinglePageStack(id, P)
35
+ + addCustomButton(config) NeoGuidebookBridge
36
+ → 上一页/下一页/首页/章节跳转
37
+ registry.submit()
38
+ → neo_guidebook.json page_ids.json
39
+ → server_form.json 供 Bridge 获取页面列表
40
+ → page_ids.json
41
+ ```
42
+
43
+ ### 脚本 ↔ JSON UI 绑定
44
+
45
+ | ActionForm API | JSON UI 变量 | 说明 |
46
+ |----------------|-------------|------|
47
+ | `.title("sapdon_ui:name")` | `#title_text` | `sapdon_ui:` 前缀路由(SapdonServerUI),精确匹配 `$panel_id` |
48
+ | `.body("page_id")` | `#form_text` | 切换可见页面(匹配页面的 `$binding_text`) |
49
+ | `.button("text")` | `#form_button_text` | 匹配按钮的 `$binding_button_text`,控制哪个按钮 visible |
50
+
51
+ > NeoGuidebook 已迁移到新接口:注册走 `SapdonServerUI.registerPage`(`panelId = sapdon_ui:<name>`),书拆为 `内容面板 + 按键面板`,导航/章节按钮使用 `SapdonTexturedButton`(模板 `server_form.sapdon_textured_button`),不再依赖旧 `ServerUISystem`/`sapdon_form_button_factory`。
52
+
53
+ ---
54
+
55
+ ## 2. NeoGuidebook 类
56
+
57
+ 用于创建指南书 UI 系统。
58
+
59
+ ```typescript
60
+ import { NeoGuidebook } from '@sapdon/core'
61
+
62
+ const book = new NeoGuidebook(identifier, path, size?, options?)
63
+ ```
64
+
65
+ ### 构造参数
66
+
67
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
68
+ |------|------|------|--------|------|
69
+ | `identifier` | `string` | 是 | — | 格式 `"命名空间:名称"`,如 `"my_mod:guidebook"` |
70
+ | `path` | `string` | 是 | — | UI 文件路径,通常 `"ui/"` |
71
+ | `size` | `[number, number]` | 否 | `[320, 207]` | 面板像素尺寸 |
72
+ | `options` | `object` | 否 | `{}` | 可选配置(见下文) |
73
+
74
+ ### options 配置项
75
+
76
+ ```typescript
77
+ {
78
+ debug?: boolean // 是否输出调试信息
79
+ buttons?: {
80
+ prev?: { visible: boolean } // 上一页按钮
81
+ next?: { visible: boolean } // 下一页按钮
82
+ home?: { visible: boolean } // 首页按钮
83
+ close?: { visible: boolean } // 关闭按钮
84
+ }
85
+ textures?: {
86
+ prevDefault?: string // 上一页默认纹理
87
+ prevHover?: string // 上一页悬停纹理
88
+ prevPressed?: string // 上一页按压纹理
89
+ nextDefault?: string // 下一页默认纹理
90
+ nextHover?: string // 下一页悬停纹理
91
+ nextPressed?: string // 下一页按压纹理
92
+ homeDefault?: string // 首页默认纹理
93
+ homeHover?: string // 首页悬停纹理
94
+ homePressed?: string // 首页按压纹理
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### 方法
100
+
101
+ #### addDoublePageStack(page_id, left_page, right_page)
102
+
103
+ 注册一个双页跨页(左右两页同时显示)。
104
+
105
+ ```typescript
106
+ book.addDoublePageStack(
107
+ 'page_index0', // page_id: string — 唯一标识,用于 body() 跳转
108
+ leftPanel, // left_page: Panel — NeoGuidebookPage.getPanel()
109
+ rightPanel, // right_page: Panel
110
+ size?: [string, string] // 可选,左右比例,默认 ["50%","50%"]
111
+ )
112
+ ```
113
+
114
+ #### addSinglePageStack(page_id, page)
115
+
116
+ 注册一个单页(占满整个表单宽度)。
117
+
118
+ ```typescript
119
+ book.addSinglePageStack(
120
+ 'page_index7', // page_id: string
121
+ pagePanel, // page: Panel
122
+ size?: [string, string] // 可选尺寸
123
+ )
124
+ ```
125
+
126
+ #### addCustomButton(config)
127
+
128
+ 添加自定义按钮。
129
+
130
+ ```typescript
131
+ book.addCustomButton({
132
+ id: 'my_button', // 按钮 ID
133
+ defaultTexture: 'textures/ui/...',
134
+ hoverTexture?: 'textures/ui/...',
135
+ pressedTexture?: 'textures/ui/...',
136
+ anchorFrom?: 'bottom_left', // 锚点
137
+ anchorTo?: 'bottom_left',
138
+ offset?: [number, number],
139
+ size?: [number | string, number | string]
140
+ })
141
+ ```
142
+
143
+ #### getPageIds()
144
+
145
+ 获取所有已注册页面的 ID 数组。
146
+
147
+ ```typescript
148
+ const ids: string[] = book.getPageIds()
149
+ // → ["page_index0", "page_index1", ...]
150
+ ```
151
+
152
+ #### getPageCount()
153
+
154
+ 获取页面总数。
155
+
156
+ ```typescript
157
+ const count: number = book.getPageCount()
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 3. NeoGuidebookPage 类
163
+
164
+ 用于构建单个页面的内容。
165
+
166
+ ```typescript
167
+ import { NeoGuidebookPage } from '@sapdon/core'
168
+
169
+ const page = new NeoGuidebookPage(id, size?)
170
+ ```
171
+
172
+ ### 构造参数
173
+
174
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
175
+ |------|------|------|--------|------|
176
+ | `id` | `string` | 是 | — | 页面唯一 ID |
177
+ | `size` | `[string, string]` | 否 | `["100%","100%"]` | 页面尺寸,百分比格式 |
178
+
179
+ ### 布局方法
180
+
181
+ 所有方法返回 `this`,支持链式调用。
182
+
183
+ #### addBookText(text, size?)
184
+
185
+ ```typescript
186
+ page.addBookText(
187
+ '这是正文内容', // text: string
188
+ ['100%', '70%'] // size?: [string, string]
189
+ )
190
+ ```
191
+
192
+ #### addCategoryTitle(title, size?)
193
+
194
+ ```typescript
195
+ page.addCategoryTitle(
196
+ '架构概述', // title: string
197
+ ['100%', '15%'] // size?: [string, string]
198
+ )
199
+ ```
200
+
201
+ #### addBookTitleBar(text, size?)
202
+
203
+ ```typescript
204
+ page.addBookTitleBar(
205
+ '欢迎使用手册', // text: string
206
+ ['100%', '15%'] // size?: [string, string]
207
+ )
208
+ ```
209
+
210
+ #### addEmptySpace(size?)
211
+
212
+ ```typescript
213
+ page.addEmptySpace(['100%', '5%'])
214
+ ```
215
+
216
+ #### addDivider(size?)
217
+
218
+ ```typescript
219
+ page.addDivider(['100%', '3%'])
220
+ ```
221
+
222
+ #### addRecipeGrid(row, col, items, size?)
223
+
224
+ ```typescript
225
+ page.addRecipeGrid(
226
+ 2, // row: number
227
+ 3, // col: number
228
+ ['textures/items/iron_ingot', ...], // items: string[]
229
+ ['100%', '30%'] // size?: [string, string]
230
+ )
231
+ ```
232
+
233
+ #### addBookCategory(title, row, col, buttons, size?)
234
+
235
+ ```typescript
236
+ page.addBookCategory(
237
+ '物品分类', // title: string
238
+ 2, // row: number
239
+ 3, // col: number
240
+ [{ id: 'tools', texture: '...' }], // buttons
241
+ ['100%', '60%'] // size?: [string, string]
242
+ )
243
+ ```
244
+
245
+ #### addChapter(name, texture)
246
+
247
+ ```typescript
248
+ page.addChapter('架构概述', 'textures/items/map')
249
+ ```
250
+
251
+ #### addChapters(chapters)
252
+
253
+ ```typescript
254
+ page.addChapters([
255
+ { chapter_name: '架构概述', chapter_texture: 'textures/items/map' },
256
+ { chapter_name: '核心类', chapter_texture: 'textures/items/iron_ingot' },
257
+ ])
258
+ ```
259
+
260
+ #### addControl(control)
261
+
262
+ 添加任意自定义 UI 控件(透传内部 StackPanel,可放任何 `UIElement` 或原生 JSON 控件对象)。
263
+
264
+ ```typescript
265
+ page.addControl(
266
+ new Label('my_label', undefined)
267
+ .setText(new Text().setText('自定义文字').setColor([0, 0, 0]))
268
+ )
269
+ ```
270
+
271
+ #### addStack(size, control, debug?)
272
+
273
+ 添加自定义控件并指定占位尺寸(透传 `StackPanel.addStack`)。
274
+
275
+ ```typescript
276
+ page.addStack(
277
+ ['100%', '20%'],
278
+ new Image('my_img', undefined).setSprite(new Sprite().setTexture('textures/items/iron_ingot'))
279
+ )
280
+ ```
281
+
282
+ #### buildChapterList(prefix?)
283
+
284
+ ```typescript
285
+ page.buildChapterList() // 默认前缀 "item",生成 item_N_button
286
+ page.buildChapterList("sub") // 多级目录:生成 sub_N_button,避免按钮 id 全局冲突
287
+ ```
288
+
289
+ `prefix` 决定目录按钮的绑定键名:`${prefix}_${index}_button`。单本书只有一个目录页时用默认值;要加子目录/二级目录,不同级别传不同前缀。
290
+
291
+ #### getPanel()
292
+
293
+ ```typescript
294
+ const panel = page.getPanel()
295
+ book.addDoublePageStack('page_0', panel, rightPanel)
296
+ ```
297
+
298
+ #### clear()
299
+
300
+ ```typescript
301
+ page.clear()
302
+ ```
303
+
304
+ ---
305
+
306
+ ## 4. NeoGuidebookBridge 类
307
+
308
+ 运行时导航管理器,封装了 ActionFormData 的显示和页面切换逻辑。
309
+
310
+ ```typescript
311
+ import { NeoGuidebookBridge } from '<path>/page_bridge'
312
+
313
+ const bridge = new NeoGuidebookBridge(uiName, pageIds, options?)
314
+ ```
315
+
316
+ ### 构造参数
317
+
318
+ | 参数 | 类型 | 必填 | 说明 |
319
+ |------|------|------|------|
320
+ | `uiName` | `string` | 是 | 书名字,必须与 `title()` 一致 |
321
+ | `pageIds` | `string[]` | 是 | 页面 ID 列表,来自 `getPageIds()` |
322
+ | `options` | `object` | 否 | `{ debug: boolean }` |
323
+
324
+ ### 方法
325
+
326
+ #### show(player, startIndex)
327
+
328
+ ```typescript
329
+ bridge.show(player, 0) // player: Player, startIndex: number
330
+ ```
331
+
332
+ #### onPage(pageId, callbacks)
333
+
334
+ ```typescript
335
+ bridge.onPage('page_index0', {
336
+ onEnter: (pageId: string, index: number) => { /* 进入页面时调用 */ },
337
+ onLeave: (pageId: string, index: number) => { /* 离开页面时调用 */ },
338
+ })
339
+ ```
340
+
341
+ ### 内置按钮映射
342
+
343
+ | ActionForm 按钮文字 | 对应 JSON UI 控件 | 行为 |
344
+ |-------------------|------------------|------|
345
+ | `"prev_button"` | 上一页按钮 | `currentIndex--` |
346
+ | `"next_button"` | 下一页按钮 | `currentIndex++` |
347
+ | `"home_button"` | 首页按钮 | `currentIndex = 0` |
348
+ | `"item_X_button"` | 章节选择按钮 | `currentIndex = X+1` |
349
+
350
+ ---
351
+
352
+ ## 5. ItemComponent 辅助
353
+
354
+ ```typescript
355
+ import { ItemComponent } from '@sapdon/core'
356
+
357
+ // 设置自定义物品组件(指南书的核心)
358
+ ItemComponent.setCustomComponentV2('sapdon:neo_guibook', {})
359
+
360
+ // 其他物品组件
361
+ ItemComponent.setMaxStackSize(1)
362
+ ItemComponent.setDisplayName('我的手册')
363
+ ItemComponent.setInteractButton('打开')
364
+ ```
365
+
366
+ ### setCustomComponentV2(componentName, options)
367
+
368
+ 参数 `componentName` 必须与运行时 `registerCustomComponent()` 注册的名字一致。这个值也会出现在物品 JSON 的 `components` 中。
369
+
370
+ ---
371
+
372
+ ## 6. page_ids.json 生成
373
+
374
+ `main.ts` 中调用 `getPageIds()` 后将结果写入文件,供运行时脚本使用:
375
+
376
+ ```typescript
377
+ const pageIds: string[] = neo_guidebook.getPageIds()
378
+ fs.writeFileSync(
379
+ path.join(process.cwd(), 'scripts', 'page_ids.json'),
380
+ JSON.stringify(pageIds, null, 2)
381
+ )
382
+ ```
383
+
384
+ 生成的 `page_ids.json` 示例:
385
+
386
+ ```json
387
+ ["page_index0", "page_index1", "page_index2", "page_index3"]
388
+ ```
389
+
390
+ ---
391
+
392
+ ## 7. 运行时注册
393
+
394
+ ```typescript
395
+ // registry.ts — v2 API
396
+ import { system, world } from "@minecraft/server"
397
+ import { GuiBookItemComponent } from "./items/gui_book"
398
+
399
+ let registered = false
400
+
401
+ system.beforeEvents.startup.subscribe((initEvent: StartupEvent) => {
402
+ if (registered) return
403
+ registered = true
404
+ initEvent.itemComponentRegistry.registerCustomComponent(
405
+ "sapdon:neo_guibook",
406
+ GuiBookItemComponent
407
+ )
408
+ })
409
+ ```
@@ -0,0 +1,185 @@
1
+ # Sapdon UI 页面壳系统 API 参考
2
+
3
+ Sapdon UI 是一套「sapdon_ui: 前缀标题路由 + 页面壳」的自定义 Server Form UI 系统。它用 TypeScript 声明式生成 `server_form.json` 路由结构与每个页面的独立 UI 文件,运行时通过 `ActionFormData` 的 title 前缀自动分流:`sapdon_ui:` 开头的标题渲染自定义全屏 UI,其它标题走原版原生表单。
4
+
5
+ ---
6
+
7
+ ## 目录
8
+
9
+ 1. [架构总览](#1-架构总览)
10
+ 2. [SapdonServerUI 类](#2-sapdonserverui-类)
11
+ 3. [SapdonPanel 类](#3-sapdonpanel-类)
12
+ 4. [SapdonButtonPanel 类](#4-sapdonbuttonpanel-类)
13
+ 5. [SapdonButton 类](#5-sapdonbutton-类)
14
+ 6. [运行时触发](#6-运行时触发)
15
+ 7. [已知注意点](#7-已知注意点)
16
+
17
+ ---
18
+
19
+ ## 1. 架构总览
20
+
21
+ ### 生成的文件(`registry.submit()` 后)
22
+
23
+ | 文件 | 内容 |
24
+ |------|------|
25
+ | `ui/server_form.json` | 路由壳:屏幕、native/自定义分流、页面壳 `custom_panel_content`、按钮模板 `form_button` |
26
+ | `ui/sapdon_ui_xxx.json` | 每个页面一个独立文件(内容面板 + 按键面板) |
27
+ | `ui/_ui_defs.json` | 自动登记所有 UI 文件 |
28
+
29
+ ### 路由结构
30
+
31
+ ```
32
+ third_party_server_screen@common.base_screen (type: screen)
33
+ └─ $screen_content = custom_full_screen
34
+ ├─ native_form@main_screen_content
35
+ │ visible = title 不含 'sapdon_ui:'
36
+ └─ sapdon_custom_full@sapdon_screen_content
37
+ visible = title 含 'sapdon_ui:'
38
+ └─ (每个注册页) @custom_panel_content ← $panel_id 精确匹配 title
39
+ ├─ content@$user_content_panel (下)
40
+ └─ buttons@$user_buttons_panel (上, 后绘制覆盖)
41
+ ```
42
+
43
+ ### 脚本 ↔ JSON UI 绑定
44
+
45
+ | ActionForm API | JSON UI 变量 | 说明 |
46
+ |----------------|-------------|------|
47
+ | `.title("sapdon_ui:xxx")` | `#title_text` | 前缀 `sapdon_ui:` = 自定义;`$panel_id` 精确匹配页面 |
48
+ | `.button("text")` | `#form_button_*` | 集合 `form_buttons`,网格按放置顺序绑定各按钮数据 |
49
+
50
+ ---
51
+
52
+ ## 2. SapdonServerUI 类
53
+
54
+ 负责生成 `server_form.json` 的完整路由壳(屏幕、分流、页面壳、form_button 模板),并提供页面注册入口。
55
+
56
+ ```typescript
57
+ import { SapdonServerUI } from '@sapdon/core'
58
+
59
+ SapdonServerUI.registerPage({
60
+ panelId: "sapdon_ui:apple", // title 精确匹配值(必须 sapdon_ui: 前缀)
61
+ name: "apple", // 页面控件名(默认 pageN)
62
+ contentPanel: "sapdon_ui_apple.apple_content_panel", // 内容面板引用
63
+ buttonsPanel: "sapdon_ui_apple.apple_buttons_panel", // 按键面板引用(可选)
64
+ })
65
+ ```
66
+
67
+ ### 参数
68
+
69
+ | 参数 | 类型 | 必填 | 说明 |
70
+ |------|------|------|------|
71
+ | `panelId` | `string` | ✅ | 触发此页的 ActionForm title(须含 `sapdon_ui:` 前缀) |
72
+ | `contentPanel` | `UIElement \| string` | ✅ | 内容面板元素或 `ns.name` 引用 |
73
+ | `buttonsPanel` | `UIElement \| string` | ❌ | 按键面板元素或引用(壳固定渲染,缺省会报引用缺失,纯内容页请传空面板) |
74
+ | `name` | `string` | ❌ | 注册在 `sapdon_screen_content` 里的控件名 |
75
+
76
+ ### 自动生成的内容
77
+
78
+ - `third_party_server_screen@common.base_screen`:`$screen_content` 指向 `custom_full_screen`,附带退出动画抑制与 `menu_cancel → menu_exit` 映射。
79
+ - `custom_full_screen`:`native_form`(`((#title_text - 'sapdon_ui:') = #title_text)` → 非自定义显示)与 `sapdon_screen_content`(取反)分流。
80
+ - `sapdon_screen_content`:`sapdon_ui:` 前缀可见,承载所有注册页。
81
+ - `custom_panel_content`:通用页面壳,`(#title_text = $panel_id)` 精确匹配,内容(下)+按键(上)两块。
82
+ - `form_button@common_buttons.light_text_button`:框架固定提供的表单按钮模板(16×16,集合绑定 `form_buttons`)。
83
+
84
+ ---
85
+
86
+ ## 3. SapdonPanel 类
87
+
88
+ 每个页面一个独立 UI 文件。把「内容面板 + 按键面板」组装进该页面的命名空间。
89
+
90
+ ```typescript
91
+ import { SapdonPanel } from '@sapdon/core'
92
+
93
+ new SapdonPanel("sapdon_ui_apple") // 生成 ui/sapdon_ui_apple.json (ns: sapdon_ui_apple)
94
+ .setContent(contentPanel) // 内容面板 UIElement
95
+ .setButtons(buttonsPanel) // 按键面板 UIElement
96
+ .build()
97
+ ```
98
+
99
+ | 方法 | 说明 |
100
+ |------|------|
101
+ | `setContent(panel)` | 设置内容面板元素(必须是 UIElement,其 id 即 `ns.xxx` 引用名) |
102
+ | `setButtons(panel)` | 设置按键面板元素 |
103
+ | `build()` | 返回生成的 `UISystem` |
104
+
105
+ > 页面的内容/按键面板元素 id 会作为 `registerPage` 里 `contentPanel`/`buttonsPanel` 的引用名,例如 `new Panel("apple_content_panel")` → `"sapdon_ui_apple.apple_content_panel"`。
106
+
107
+ ---
108
+
109
+ ## 4. SapdonButtonPanel 类
110
+
111
+ 构建按键网格面板(grid)。网格通过 `collection_name: form_buttons` 提供每项数据上下文,`place()` 把按钮按 `grid_position` 摆到指定格(内部即 pos_wrap:面板包裹 + `grid_position`)。
112
+
113
+ ```typescript
114
+ import { SapdonButtonPanel, SapdonButton } from '@sapdon/core'
115
+
116
+ const buttons = new SapdonButtonPanel("apple_buttons_panel")
117
+ .setDimensions([2, 1]) // [列, 行]
118
+ .setCollection("form_buttons") // 注入表单按钮集合
119
+ .setSize(["100%", "100%"])
120
+ .place([0, 0], new SapdonButton("bt0").setAnchor("bottom_left"))
121
+ .place([1, 0], new SapdonButton("bt1").setAnchor("bottom_right"))
122
+ .build()
123
+ ```
124
+
125
+ | 方法 | 说明 |
126
+ |------|------|
127
+ | `setDimensions([cols, rows])` | 网格尺寸(**列 × 行**) |
128
+ | `setCollection(name)` | 注入集合(表单场景固定 `form_buttons`) |
129
+ | `setSize(size)` | 网格尺寸 |
130
+ | `place([col, row], element)` | 摆一个按钮/控件到指定格(自动包一层 pos_wrap) |
131
+ | `build()` | 返回 `Grid` |
132
+
133
+ > **布局技巧**:`grid_dimensions [2,1]` 把面板切成左右两份;格子内控件默认左上对齐,改锚点 `bottom_left` / `bottom_right` / `top_right` 即可把按钮贴到对应角。
134
+
135
+ ---
136
+
137
+ ## 5. SapdonButton 类
138
+
139
+ 表单按钮的封装:默认模板固定为 `server_form.form_button`(吃 `form_buttons` 集合数据),无需手写模板引用。
140
+
141
+ ```typescript
142
+ import { SapdonButton } from '@sapdon/core'
143
+
144
+ new SapdonButton("bt0") // → bt0@server_form.form_button
145
+ .setAnchor("bottom_left") // 锚点对齐,默认 32×32
146
+ ```
147
+
148
+ | 方法 | 说明 |
149
+ |------|------|
150
+ | `setAnchor(anchor)` | 设置 `anchor_from`/`anchor_to` 对齐(如 `bottom_left`、`bottom_right`、`top_right`),默认尺寸 32×32 |
151
+
152
+ > 右上角的「退出」这类非集合按钮,请用普通 `Button("exit", "common.button")` + `$pressed_button_name: "button.menu_exit"` 手写,不占用表单按钮集合。
153
+
154
+ ---
155
+
156
+ ## 6. 运行时触发
157
+
158
+ ```typescript
159
+ import { world } from "@minecraft/server"
160
+ import { ActionFormData } from "@minecraft/server-ui"
161
+
162
+ world.afterEvents.itemUse.subscribe((event) => {
163
+ if (event.itemStack.typeId == "minecraft:apple") {
164
+ new ActionFormData()
165
+ .title("sapdon_ui:apple") // 前缀路由 → 自定义页
166
+ .body("...")
167
+ .button("test1").button("test2") // 喂给集合 form_buttons
168
+ .show(event.source)
169
+ }
170
+ })
171
+ ```
172
+
173
+ - title 含 `sapdon_ui:` → 自定义全屏 UI;否则原版原生表单。
174
+ - 自定义页内的按钮点击通过 `button.form_button_click` 返回 `response.selection`(对应集合下标)。
175
+
176
+ ---
177
+
178
+ ## 7. 已知注意点
179
+
180
+ 1. **`grid_position` 顺序**:本项目约定为 `[列, 行]`(与 `grid_dimensions [列, 行]` 一致)。
181
+ 2. **grid 内 `offset` 无效**:网格接管子控件定位;要偏移请包一层 panel(pos_wrap),offset 放内层。
182
+ 3. **`collection_index` 不是合法 UI 属性**:引擎会报 `Unknown property [collection_index]`。每个按钮对应哪个表单按钮,靠 grid 的集合上下文按放置顺序确定,不要用 `collection_index`。
183
+ 4. **壳固定渲染 content+buttons 两块**:纯内容页也必须提供一个(空的)按键面板,否则 `$user_buttons_panel` 引用缺失报错。
184
+ 5. **grid 数量绑定**:`#maximum_grid_items` 需 int(用 `#form_button_length`);collection_panel 才用数组 `#form_button_contents` → `#collection_length`。
185
+ 6. **裸引用**:页面壳里的 `content@$user_content_panel` / `buttons@$user_buttons_panel` 必须是裸引用(不带 `type`),否则网格类型被覆盖导致 `grid_dimensions`/`grid_position` 报未知属性。
@@ -9,7 +9,7 @@
9
9
  "formatVersion": 2,
10
10
  "buildOptions": {
11
11
  "useHMR": true, // 是否启用热更新
12
- "buildMode": "development", // "development" | "production"
12
+ "buildMode": "dev", // "dev" | "prod" | "debug"
13
13
  "buildEntry": "main.ts", // Addon 构建入口文件
14
14
  "useJs": false, // 是否使用 JavaScript
15
15
  "scriptEntry": "scripts/main.ts", // Script API 入口
@@ -39,10 +39,9 @@
39
39
 
40
40
  | 值 | 说明 |
41
41
  |----|------|
42
- | `development` | **完整构建**:运行 `main.ts` → 根据代码生成所有 JSON → 打包脚本 → 同步到 Minecraft 目录。Script API 输出带 sourcemap,不压缩。 |
43
- | `production` | **仅同步**:跳过 `main.ts` 执行和 JSON 生成,将已有 `dev/` 目录直接同步到 Minecraft 目录。适合直接编辑 JSON 文件测试时使用。 |
44
-
45
- > 注意:`production` 模式不会运行 `main.ts`,也不会生成物品/实体/方块/配方的 JSON 文件。确保 `dev/` 目录已有正确的 JSON 文件(通常从一次 `development` 构建中获得)。
42
+ | `dev` | **完整构建**:运行 `main.ts` → 根据代码生成所有 JSON → 打包脚本 → 同步到 Minecraft 目录。Script API 输出带 sourcemap,不压缩。 |
43
+ | `prod` | **生产构建**:运行 `main.ts` 生成 JSON 打包脚本(terser 压缩混淆)→ 同步。不生成 sourcemap。 |
44
+ | `debug` | **仅同步**:跳过 `main.ts` 执行和 JSON 生成,跳过脚本压缩,将已有 `dev/` 目录直接同步到 Minecraft 目录。适合直接编辑 JSON 文件测试时使用。 |
46
45
 
47
46
  ### buildEntry
48
47
  构建入口文件路径(相对项目根目录)。文件中的 `registry.submit()` 会将注册数据提交到构建系统。
@@ -40,7 +40,8 @@ hello_sapdon/
40
40
  ├── pack_icon.png # 模组图标
41
41
  ├── res/ # 资源文件(纹理、模型、音效)
42
42
  └── scripts/
43
- └── main.ts # Script API 入口(游戏内逻辑)
43
+ ├── index.ts # 脚本入口——注册组件 + 游戏内逻辑
44
+ └── main.ts # 游戏主类
44
45
  ```
45
46
 
46
47
  ## 编写代码
@@ -48,12 +49,12 @@ hello_sapdon/
48
49
  打开 `main.ts`,编写以下代码:
49
50
 
50
51
  ```typescript
51
- import { ItemAPI, registry, ItemComponent } from '@sapdon/core'
52
+ import { ItemAPI, ItemCategory, registry, ItemComponent } from '@sapdon/core'
52
53
 
53
54
  // 创建一个基础物品
54
55
  const item = ItemAPI.createItem(
55
56
  'hello_sapdon:my_item',
56
- 'items',
57
+ ItemCategory.Items,
57
58
  'masterball'
58
59
  )
59
60
  item.addComponent(ItemComponent.setDisplayName('我的物品'))
@@ -75,14 +76,25 @@ sapdon build .
75
76
 
76
77
  | 模式 | 行为 |
77
78
  |------|------|
78
- | `development`(默认) | 运行 `main.ts` → 根据代码生成所有 JSON → 打包脚本 同步到 Minecraft |
79
- | `production` | 跳过 `main.ts`,直接同步已有 `dev/` 目录到 Minecraft(适合直接编辑 JSON 测试) |
79
+ | `dev`(默认) | 运行 `main.ts` → 根据代码生成所有 JSON → 打包脚本(不压缩)→ 同步到 Minecraft |
80
+ | `prod` | 运行 `main.ts` 生成 JSON 打包脚本(terser 压缩)→ 同步 |
81
+ | `debug` | 跳过 `main.ts`,跳过压缩,直接同步已有 `dev/` 目录到 Minecraft(适合直接编辑 JSON 测试) |
82
+
83
+ ```bash
84
+ # 使用 sapdon compile 以 production 模式构建(不启动热更新)
85
+ sapdon compile
86
+ ```
87
+
88
+ ### 打包为 .mcaddon
89
+
90
+ 构建完成后,可将输出打包为 `.mcaddon` 文件,方便分发:
80
91
 
81
92
  ```bash
82
- # 使用 sapdon pack 以 production 模式构建(不启动热更新)
83
93
  sapdon pack
84
94
  ```
85
95
 
96
+ 输出文件:`dev/<项目名>.mcaddon`
97
+
86
98
  ## 构建输出
87
99
 
88
100
  构建完成后,`dev/` 目录下生成:
@@ -105,4 +117,6 @@ dev/
105
117
  - [物品教程](./tutorials/item.md) — 学习创建各类物品
106
118
  - [实体教程](./tutorials/entity.md) — 学习创建实体
107
119
  - [方块教程](./tutorials/block.md) — 学习创建方块
120
+ - [指南书教程](./tutorials/neo-guidebook.md) — API 用法
121
+ - [指南书实战经验](./tutorials/neo-guidebook-experience.md) — 接入流程与踩坑清单
108
122
  - [API 参考](./api/item.md) — 完整的 API 文档