xinyu-pro 0.22.6 → 0.23.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.
- package/app/api/chat/route.ts +2 -2
- package/app/api/generate-svg/route.ts +1 -1
- package/app/api/generate-theme/route.ts +112 -135
- package/app/api/plugins/bindings/route.ts +4 -4
- package/app/api/plugins/export/route.ts +9 -3
- package/app/api/plugins/export-xye/route.ts +12 -7
- package/app/api/plugins/files/route.ts +9 -9
- package/app/api/plugins/files-list/route.ts +7 -3
- package/app/api/plugins/files-upload/route.ts +3 -3
- package/app/api/plugins/files-write/all/route.ts +250 -0
- package/app/api/plugins/files-write/route.ts +13 -15
- package/app/api/plugins/import/route.ts +5 -5
- package/app/api/plugins/import-package/route.ts +9 -9
- package/app/api/plugins/resources/route.ts +6 -2
- package/app/api/plugins/route.ts +215 -119
- package/app/api/plugins/scan/route.ts +1 -1
- package/app/api/plugins/storage/route.ts +3 -3
- package/app/api/plugins/toggle/route.ts +21 -0
- package/app/api/sessions/route.ts +8 -3
- package/app/api/settings/route.ts +2 -2
- package/app/api/suggest-fields/route.ts +1 -1
- package/app/api/templates/route.ts +2 -2
- package/app/api/themes/route.ts +116 -0
- package/app/editor/page.tsx +21 -4
- package/app/extensions/edit/[id]/page.tsx +2148 -984
- package/app/extensions/page.tsx +477 -86
- package/app/extensions/tutorial/code-examples.ts +833 -0
- package/app/extensions/tutorial/nav-link.tsx +27 -0
- package/app/extensions/tutorial/page.tsx +47 -3616
- package/app/extensions/tutorial/sections/AiInterceptionSection.tsx +87 -0
- package/app/extensions/tutorial/sections/ArchitectureSection.tsx +39 -0
- package/app/extensions/tutorial/sections/BestPracticesSection.tsx +78 -0
- package/app/extensions/tutorial/sections/ChatApiSection.tsx +212 -0
- package/app/extensions/tutorial/sections/CrossPluginSection.tsx +90 -0
- package/app/extensions/tutorial/sections/CssVariablesSection.tsx +189 -0
- package/app/extensions/tutorial/sections/DebugSection.tsx +48 -0
- package/app/extensions/tutorial/sections/DependencySection.tsx +134 -0
- package/app/extensions/tutorial/sections/DomSandboxSection.tsx +229 -0
- package/app/extensions/tutorial/sections/ExamplesSection.tsx +29 -0
- package/app/extensions/tutorial/sections/FileApiSection.tsx +44 -0
- package/app/extensions/tutorial/sections/GlobalApiSection.tsx +50 -0
- package/app/extensions/tutorial/sections/InputApiSection.tsx +213 -0
- package/app/extensions/tutorial/sections/LifecycleSection.tsx +105 -0
- package/app/extensions/tutorial/sections/OverviewSection.tsx +54 -0
- package/app/extensions/tutorial/sections/PermissionsSection.tsx +239 -0
- package/app/extensions/tutorial/sections/PersistentStorageSection.tsx +49 -0
- package/app/extensions/tutorial/sections/PluginConfigSection.tsx +81 -0
- package/app/extensions/tutorial/sections/PluginImportExportSection.tsx +68 -0
- package/app/extensions/tutorial/sections/PluginStorageSection.tsx +94 -0
- package/app/extensions/tutorial/sections/ResourceApiSection.tsx +88 -0
- package/app/extensions/tutorial/sections/SecuritySection.tsx +106 -0
- package/app/extensions/tutorial/sections/SlotsSection.tsx +130 -0
- package/app/extensions/tutorial/sections/UiApiSection.tsx +147 -0
- package/app/extensions/tutorial/sections/UtilsApiSection.tsx +96 -0
- package/app/extensions/tutorial/sections-data.tsx +325 -0
- package/app/extensions/tutorial/styles.ts +59 -0
- package/app/extensions/tutorial/types.ts +14 -0
- package/app/game/[id]/page.tsx +336 -148
- package/app/globals.css +41 -0
- package/app/page.tsx +17 -10
- package/app/settings/page.tsx +218 -236
- package/bin/cli.js +3 -10
- package/components/ChatInput.tsx +20 -6
- package/components/ChatWindow.tsx +1 -1
- package/components/MessageBubble.tsx +5 -6
- package/components/ui/ConfirmDialog.tsx +28 -6
- package/components/ui/DeprecatedBadge.tsx +163 -0
- package/components/ui/PageHeader.tsx +1 -1
- package/components/ui/PermissionConflictDialog.tsx +1 -1
- package/components/ui/PluginConfigForm.tsx +2 -3
- package/components/ui/PluginFloatingLayer.tsx +2 -2
- package/components/ui/PluginModalRenderer.tsx +84 -38
- package/components/ui/PluginProvider.tsx +32 -13
- package/components/ui/PluginSlotRenderer.tsx +2 -2
- package/components/ui/ThemeCustomizer.tsx +4 -7
- package/components/ui/ThemeProvider.tsx +214 -125
- package/components/ui/ThemeSwitcher.tsx +119 -157
- package/components/ui/ToastProvider.tsx +47 -17
- package/lib/{parseWorldCard.ts → client/parseWorldCard.ts} +1 -1
- package/lib/{plugin-dom-sandbox.ts → client/plugin-dom-sandbox.ts} +2 -2
- package/lib/{plugin-runtime.ts → client/plugin-runtime.ts} +166 -67
- package/lib/client/plugin-utils.ts +105 -0
- package/lib/{router-history.ts → client/router-history.ts} +1 -1
- package/lib/{storage.ts → client/storage.ts} +66 -3
- package/lib/{themes.ts → client/themes.ts} +21 -1
- package/lib/{builtin-plugins.ts → server/builtin-plugins.ts} +1 -1
- package/lib/{db-init.ts → server/db-init.ts} +2 -2
- package/lib/{db.ts → server/db.ts} +48 -8
- package/lib/{get-ai-config.ts → server/get-ai-config.ts} +2 -2
- package/lib/{plugin-files.ts → server/plugin-files.ts} +7 -2
- package/lib/server/plugin-utils.ts +88 -0
- package/lib/{manifest-parser.ts → shared/manifest-parser.ts} +4 -4
- package/lib/{plugin-types.ts → shared/plugin-types.ts} +11 -6
- package/lib/{version.ts → shared/version.ts} +1 -1
- package/next.config.mjs +3 -2
- package/package.json +4 -3
- package/styles/themes.css +7 -2
- package/version.json +2 -2
- package/app/extensions/create/page.tsx +0 -1422
- /package/lib/{plugin-events.ts → client/plugin-events.ts} +0 -0
- /package/lib/{plugin-resource-tracker.ts → client/plugin-resource-tracker.ts} +0 -0
- /package/lib/{plugin-html-sanitizer.ts → shared/plugin-html-sanitizer.ts} +0 -0
- /package/lib/{plugin-security.ts → shared/plugin-security.ts} +0 -0
- /package/lib/{prompt-builder.ts → shared/prompt-builder.ts} +0 -0
- /package/lib/{types.ts → shared/types.ts} +0 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
// app/extensions/tutorial/code-examples.ts - 教程页面代码块常量
|
|
2
|
+
|
|
3
|
+
// ==================== 代码块常量 ====================
|
|
4
|
+
|
|
5
|
+
export const CODE_OVERVIEW_MINIMAL = [
|
|
6
|
+
'function setup(xinyu) {',
|
|
7
|
+
" // 插件加载时执行",
|
|
8
|
+
" xinyu.ui.toast('插件已启用!', 'success');",
|
|
9
|
+
'}',
|
|
10
|
+
].join('\n');
|
|
11
|
+
|
|
12
|
+
export const CODE_ARCHITECTURE_EXEC = [
|
|
13
|
+
'// 运行时内部执行方式(简化)',
|
|
14
|
+
'const wrappedCode = `',
|
|
15
|
+
" 'use strict';",
|
|
16
|
+
' ${pluginCode};',
|
|
17
|
+
" if (typeof setup === 'function') {",
|
|
18
|
+
' return { setup };',
|
|
19
|
+
' }',
|
|
20
|
+
' return {};',
|
|
21
|
+
'`;',
|
|
22
|
+
"const factory = new Function('xinyu', wrappedCode);",
|
|
23
|
+
'const exports = factory(xinyuBridge);',
|
|
24
|
+
'exports.setup(xinyuBridge); // 调用插件入口',
|
|
25
|
+
].join('\n');
|
|
26
|
+
|
|
27
|
+
export const CODE_LIFECYCLE_EXAMPLE = [
|
|
28
|
+
'function setup(xinyu) {',
|
|
29
|
+
" xinyu.plugin.on('onLoad', function() {",
|
|
30
|
+
" xinyu.ui.toast('插件已加载', 'success');",
|
|
31
|
+
' });',
|
|
32
|
+
'',
|
|
33
|
+
" xinyu.plugin.on('onGameInit', function() {",
|
|
34
|
+
' xinyu.game.registerAttribute({',
|
|
35
|
+
" key: 'hp', label: '生命值', type: 'number',",
|
|
36
|
+
" value: 100, icon: '❤️'",
|
|
37
|
+
' });',
|
|
38
|
+
' });',
|
|
39
|
+
'',
|
|
40
|
+
" xinyu.plugin.on('onUnload', function() {",
|
|
41
|
+
' // 资源会自动清理,通常不需要手动处理',
|
|
42
|
+
' });',
|
|
43
|
+
'}',
|
|
44
|
+
].join('\n');
|
|
45
|
+
|
|
46
|
+
export const CODE_UI_SLOT = [
|
|
47
|
+
'// 在状态栏显示自定义信息',
|
|
48
|
+
"xinyu.ui.registerSlot('status-bar',",
|
|
49
|
+
' \'<div style="font-size:11px;color:var(--color-text-muted)">自定义状态</div>\',',
|
|
50
|
+
' { priority: 1 }',
|
|
51
|
+
');',
|
|
52
|
+
'',
|
|
53
|
+
'// 在顶部导航栏右侧注入元素',
|
|
54
|
+
"xinyu.ui.registerSlot('header-right',",
|
|
55
|
+
' \'<span style="font-size:12px">🔔 3</span>\',',
|
|
56
|
+
' { priority: 10 }',
|
|
57
|
+
');',
|
|
58
|
+
'',
|
|
59
|
+
'// 移除注册的内容',
|
|
60
|
+
'xinyu.ui.unregisterSlot(registrationId);',
|
|
61
|
+
].join('\n');
|
|
62
|
+
|
|
63
|
+
export const CODE_UI_MODAL = [
|
|
64
|
+
'// 确认对话框(返回 Promise<boolean>)',
|
|
65
|
+
'const ok = await xinyu.ui.confirm(',
|
|
66
|
+
" '确认操作',",
|
|
67
|
+
" '确定要重置属性吗?',",
|
|
68
|
+
" { confirmText: '重置', cancelText: '取消', danger: true }",
|
|
69
|
+
');',
|
|
70
|
+
'',
|
|
71
|
+
'// 自定义模态框',
|
|
72
|
+
'const modalId = xinyu.ui.showModal({',
|
|
73
|
+
" title: '角色属性编辑',",
|
|
74
|
+
" content: '<div style=\"padding:12px\">自定义内容</div>',",
|
|
75
|
+
" width: '400px',",
|
|
76
|
+
' closable: true,',
|
|
77
|
+
' backdrop: true,',
|
|
78
|
+
" style: 'border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);', // 自定义模态框容器样式",
|
|
79
|
+
' actions: [',
|
|
80
|
+
" {" ,
|
|
81
|
+
" text: '保存'," ,
|
|
82
|
+
" primary: true," ,
|
|
83
|
+
" onClick: function(ctx) { /* 保存逻辑 */ ctx.close(); }," ,
|
|
84
|
+
" closeModal: true // 若 onClick 中调用了 ctx.close(),那么需要设置 closeModal 为 true 来启用模态框的关闭动画" ,
|
|
85
|
+
" },",
|
|
86
|
+
" { text: '取消', onClick: function(ctx) { ctx.close(); }, closeModal: true }",
|
|
87
|
+
' ],',
|
|
88
|
+
' onClose: function() { /* 关闭回调 */ }',
|
|
89
|
+
'});',
|
|
90
|
+
'',
|
|
91
|
+
'// 更新模态框(无需关闭重开,可动态修改 title、content、width、style)',
|
|
92
|
+
'xinyu.ui.updateModal(modalId, {',
|
|
93
|
+
" title: '角色属性编辑(已修改)',",
|
|
94
|
+
" content: '<div style=\"padding:12px\">更新后的内容</div>',",
|
|
95
|
+
" width: '500px',",
|
|
96
|
+
" style: 'border-radius: 16px; background: #1a1a2e;',",
|
|
97
|
+
'});',
|
|
98
|
+
'',
|
|
99
|
+
'// 关闭模态框',
|
|
100
|
+
'xinyu.ui.closeModal(modalId);',
|
|
101
|
+
].join('\n');
|
|
102
|
+
|
|
103
|
+
// 确认对话框详细示例
|
|
104
|
+
export const CODE_CONFIRM_DETAIL = [
|
|
105
|
+
'// 基础用法:弹出确认对话框,等待用户选择',
|
|
106
|
+
'const confirmed = await xinyu.ui.confirm(',
|
|
107
|
+
" '删除存档',",
|
|
108
|
+
" '此操作不可撤销,确定要删除当前游戏存档吗?',",
|
|
109
|
+
');',
|
|
110
|
+
'if (confirmed) {',
|
|
111
|
+
" xinyu.ui.toast('存档已删除', 'success');",
|
|
112
|
+
'} else {',
|
|
113
|
+
" xinyu.ui.toast('已取消', 'info');",
|
|
114
|
+
'}',
|
|
115
|
+
'',
|
|
116
|
+
'// 自定义按钮文字和危险样式(红色确认按钮)',
|
|
117
|
+
'const ok = await xinyu.ui.confirm(',
|
|
118
|
+
" '重置全部属性',",
|
|
119
|
+
" '将所有角色属性恢复为初始值,是否继续?',",
|
|
120
|
+
" { confirmText: '确定重置', cancelText: '再想想', danger: true }",
|
|
121
|
+
');',
|
|
122
|
+
'',
|
|
123
|
+
'// 在事件处理器中使用 confirm',
|
|
124
|
+
"xinyu.chat.registerCommand({",
|
|
125
|
+
" name: '/reset',",
|
|
126
|
+
" description: '重置游戏进度',",
|
|
127
|
+
" handler: async function() {",
|
|
128
|
+
" const ok = await xinyu.ui.confirm(",
|
|
129
|
+
" '⚠️ 危险操作',",
|
|
130
|
+
" '重置后所有进度将丢失!',",
|
|
131
|
+
" { danger: true }",
|
|
132
|
+
" );",
|
|
133
|
+
" if (ok) {",
|
|
134
|
+
" xinyu.game.setState({ gold: 0, level: 1 });",
|
|
135
|
+
" xinyu.ui.toast('进度已重置', 'warning');",
|
|
136
|
+
" return '【系统】游戏进度已被重置';",
|
|
137
|
+
" }",
|
|
138
|
+
" return '【系统】取消重置操作';",
|
|
139
|
+
" }",
|
|
140
|
+
'});',
|
|
141
|
+
].join('\n');
|
|
142
|
+
|
|
143
|
+
export const CODE_UI_STYLE = [
|
|
144
|
+
'// 注入自定义 CSS(使用 CSS 变量保持主题一致)',
|
|
145
|
+
'const styleId = xinyu.ui.injectStyle(`',
|
|
146
|
+
' .my-plugin-panel {',
|
|
147
|
+
' background: var(--color-bg-tertiary);',
|
|
148
|
+
' border: 1px solid var(--color-border);',
|
|
149
|
+
' border-radius: 8px;',
|
|
150
|
+
' padding: 12px;',
|
|
151
|
+
' }',
|
|
152
|
+
' .my-plugin-highlight {',
|
|
153
|
+
' color: var(--color-accent);',
|
|
154
|
+
' font-weight: bold;',
|
|
155
|
+
' }',
|
|
156
|
+
'`);',
|
|
157
|
+
'',
|
|
158
|
+
'// 需要时移除',
|
|
159
|
+
'xinyu.ui.removeStyle(styleId);',
|
|
160
|
+
].join('\n');
|
|
161
|
+
|
|
162
|
+
export const CODE_UI_HOST_EVENT = [
|
|
163
|
+
'// 监听消息接收事件',
|
|
164
|
+
"xinyu.ui.onHostEvent('message:received', function(data) {",
|
|
165
|
+
" console.log('新消息:', data.message);",
|
|
166
|
+
'});',
|
|
167
|
+
'',
|
|
168
|
+
'// 监听游戏状态变更',
|
|
169
|
+
"xinyu.ui.onHostEvent('game:stateChange', function(data) {",
|
|
170
|
+
" console.log('变更的键:', data.changedKeys);",
|
|
171
|
+
'});',
|
|
172
|
+
'',
|
|
173
|
+
'// 可用事件:',
|
|
174
|
+
'// message:received / message:sent / game:stateChange',
|
|
175
|
+
'// sidebar:open / sidebar:close / input:focus / input:blur',
|
|
176
|
+
'// theme:change / plugin:enabled / plugin:disabled',
|
|
177
|
+
].join('\n');
|
|
178
|
+
|
|
179
|
+
export const CODE_DOM_API = [
|
|
180
|
+
'// 创建元素',
|
|
181
|
+
"const el = xinyu.ui.dom.create('div', {",
|
|
182
|
+
" className: 'my-panel',",
|
|
183
|
+
" style: 'position:absolute;top:10px;right:10px;',",
|
|
184
|
+
"}, 'Hello World');",
|
|
185
|
+
'',
|
|
186
|
+
'// 追加到容器(支持插槽 ID)',
|
|
187
|
+
"xinyu.ui.dom.append('floating', el);",
|
|
188
|
+
'',
|
|
189
|
+
'// 也可以直接追加 HTML 字符串',
|
|
190
|
+
"xinyu.ui.dom.append('floating', '<div class=\"my-panel\">Hello</div>');",
|
|
191
|
+
'',
|
|
192
|
+
'// 更新元素',
|
|
193
|
+
'xinyu.ui.dom.update(el.id, {',
|
|
194
|
+
" style: 'position:absolute;top:20px;',",
|
|
195
|
+
" textContent: 'Updated!'",
|
|
196
|
+
'});',
|
|
197
|
+
'',
|
|
198
|
+
'// 查询元素(仅限插件自己的元素)',
|
|
199
|
+
"const results = xinyu.ui.dom.query('floating', '.my-panel');",
|
|
200
|
+
'',
|
|
201
|
+
'// 绑定事件',
|
|
202
|
+
"xinyu.ui.dom.on(el.id, 'click', function() {",
|
|
203
|
+
" xinyu.ui.toast('被点击了!', 'info');",
|
|
204
|
+
'});',
|
|
205
|
+
'',
|
|
206
|
+
'// 移除元素',
|
|
207
|
+
'xinyu.ui.dom.remove(el.id);',
|
|
208
|
+
].join('\n');
|
|
209
|
+
|
|
210
|
+
export const CODE_PERMS_DEFAULT = "['slot:input-toolbar', 'style:inject', 'modal:confirm']";
|
|
211
|
+
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
213
|
+
export const CODE_PERMS_MANIFEST = [
|
|
214
|
+
'// 在插件代码中查看自身权限',
|
|
215
|
+
'const manifest = xinyu.plugin.getManifest();',
|
|
216
|
+
'console.log(manifest.commonPermissions);',
|
|
217
|
+
'console.log(manifest.exclusivePermissions);',
|
|
218
|
+
'console.log(manifest.requiredPermissions);',
|
|
219
|
+
].join('\n');
|
|
220
|
+
|
|
221
|
+
export const CODE_SLOTS_DIAGRAM = [
|
|
222
|
+
'┌──────────────────────────────────────────────────┐',
|
|
223
|
+
'│ [header-left] [header-center] [header-right] │ ← 顶部导航栏(header)',
|
|
224
|
+
'├──────────────┬───────────────────┬───────────────┤',
|
|
225
|
+
'│ │ [message-top] │ │ ← 消息上方(message-top)',
|
|
226
|
+
'│ │ │ │',
|
|
227
|
+
'│ │ Message List Area │ │',
|
|
228
|
+
'│[sidebar-left]│ │[sidebar-right]│',
|
|
229
|
+
'│ │ [message-bottom] │ │ ← 消息下方(message-bottom)',
|
|
230
|
+
'│ Left Side │ [input-above] │ Right Side │ ← 输入框正上方(input-above)',
|
|
231
|
+
'│ Bar Area │ [input-toolbar] │ Bar Area │ ← 工具栏(input-toolbar)',
|
|
232
|
+
'│ ├───────────────────┤ │',
|
|
233
|
+
'│ │ Input Area │ │',
|
|
234
|
+
'│ ├───────────────────┤ │',
|
|
235
|
+
'│ │ [status-bar] │ │ ← 底部状态栏(status-bar)',
|
|
236
|
+
'└──────────────┴───────────────────┴───────────────┘',
|
|
237
|
+
'[model] ← 模态框(由平台系统控制)',
|
|
238
|
+
'[floating] ← 浮动层(fixed 定位,自由放置)',
|
|
239
|
+
'[overlay] ← 全屏遮罩层(最高 z-index)',
|
|
240
|
+
].join('\n');
|
|
241
|
+
|
|
242
|
+
export const CODE_SLOTS_PRIORITY = [
|
|
243
|
+
"xinyu.ui.registerSlot('status-bar', '插件A', { priority: 1 }); // 最前",
|
|
244
|
+
"xinyu.ui.registerSlot('status-bar', '插件B', { priority: 10 }); // 中间",
|
|
245
|
+
"xinyu.ui.registerSlot('status-bar', '插件C'); // 最后(默认100)",
|
|
246
|
+
].join('\n');
|
|
247
|
+
|
|
248
|
+
export const CODE_EXAMPLE_1 = [
|
|
249
|
+
'function setup(xinyu) {',
|
|
250
|
+
' // 在状态栏显示游戏时间',
|
|
251
|
+
' const startTime = Date.now();',
|
|
252
|
+
'',
|
|
253
|
+
" xinyu.ui.registerSlot('status-bar',",
|
|
254
|
+
' \'<div id="game-timer" style="font-size:11px;color:var(--color-text-muted)">\' +',
|
|
255
|
+
" '⏱️ 游戏时间: 0分0秒</div>',",
|
|
256
|
+
' { priority: 1 }',
|
|
257
|
+
' );',
|
|
258
|
+
'',
|
|
259
|
+
' // 每秒更新',
|
|
260
|
+
' setInterval(function() {',
|
|
261
|
+
' const elapsed = Math.floor((Date.now() - startTime) / 1000);',
|
|
262
|
+
' const min = Math.floor(elapsed / 60);',
|
|
263
|
+
' const sec = elapsed % 60;',
|
|
264
|
+
" const el = document.querySelector('#game-timer');",
|
|
265
|
+
" if (el) el.textContent = '⏱️ 游戏时间: ' + min + '分' + sec + '秒';",
|
|
266
|
+
' }, 1000);',
|
|
267
|
+
'}',
|
|
268
|
+
].join('\n');
|
|
269
|
+
|
|
270
|
+
export const CODE_EXAMPLE_2 = [
|
|
271
|
+
'function setup(xinyu) {',
|
|
272
|
+
' const msgCount = 0;',
|
|
273
|
+
'',
|
|
274
|
+
" xinyu.ui.onHostEvent('message:received', function() {",
|
|
275
|
+
' msgCount++;',
|
|
276
|
+
' // 更新状态栏',
|
|
277
|
+
" xinyu.ui.registerSlot('status-bar',",
|
|
278
|
+
' \'<div style="font-size:11px;color:var(--color-text-muted)">\' +',
|
|
279
|
+
" '💬 已收到 ' + msgCount + ' 条消息</div>',",
|
|
280
|
+
' { priority: 2 }',
|
|
281
|
+
' );',
|
|
282
|
+
' });',
|
|
283
|
+
'',
|
|
284
|
+
' // 注册一个查看统计的按钮',
|
|
285
|
+
' xinyu.ui.registerInputToolbarButton({',
|
|
286
|
+
" id: 'msg-stats',",
|
|
287
|
+
" label: '统计',",
|
|
288
|
+
" icon: '📊',",
|
|
289
|
+
' order: 20,',
|
|
290
|
+
' onClick: function() {',
|
|
291
|
+
' xinyu.ui.showModal({',
|
|
292
|
+
" title: '📊 消息统计',",
|
|
293
|
+
' content: \'<div style="padding:16px;text-align:center">\' +',
|
|
294
|
+
' \'<div style="font-size:32px;font-weight:bold;color:var(--color-accent)">\' + msgCount + \'</div>\' +',
|
|
295
|
+
" '<div style=\"color:var(--color-text-muted);margin-top:4px\">已接收消息总数</div></div>',",
|
|
296
|
+
" width: '300px'",
|
|
297
|
+
' });',
|
|
298
|
+
' }',
|
|
299
|
+
' });',
|
|
300
|
+
'}',
|
|
301
|
+
].join('\n');
|
|
302
|
+
|
|
303
|
+
export const CODE_EXAMPLE_3 = [
|
|
304
|
+
'function setup(xinyu) {',
|
|
305
|
+
' // 注入样式',
|
|
306
|
+
' xinyu.ui.injectStyle(`',
|
|
307
|
+
' .quick-menu {',
|
|
308
|
+
' position: fixed; bottom: 80px; right: 20px;',
|
|
309
|
+
' display: flex; flex-direction: column; gap: 8px;',
|
|
310
|
+
' z-index: 50;',
|
|
311
|
+
' }',
|
|
312
|
+
' .quick-menu-btn {',
|
|
313
|
+
' width: 44px; height: 44px; border-radius: 50%;',
|
|
314
|
+
' background: var(--color-bg-secondary);',
|
|
315
|
+
' border: 1px solid var(--color-border);',
|
|
316
|
+
' color: var(--color-text-secondary);',
|
|
317
|
+
' font-size: 18px; cursor: pointer;',
|
|
318
|
+
' display: flex; align-items: center; justify-content: center;',
|
|
319
|
+
' transition: all 0.2s;',
|
|
320
|
+
' }',
|
|
321
|
+
' .quick-menu-btn:hover {',
|
|
322
|
+
' background: var(--color-accent); color: #fff;',
|
|
323
|
+
' transform: scale(1.1);',
|
|
324
|
+
' }',
|
|
325
|
+
' `);',
|
|
326
|
+
'',
|
|
327
|
+
' // 在浮动层注入按钮',
|
|
328
|
+
" xinyu.ui.registerSlot('floating',",
|
|
329
|
+
" '<div class=\"quick-menu\">' +",
|
|
330
|
+
" '<button class=\"quick-menu-btn\" onclick=\"document.dispatchEvent(new CustomEvent(\\'plugin-action\\', {detail:\\'look\\'}))\">👁️</button>' +",
|
|
331
|
+
" '<button class=\"quick-menu-btn\" onclick=\"document.dispatchEvent(new CustomEvent(\\'plugin-action\\', {detail:\\'talk\\'}))\">💬</button>' +",
|
|
332
|
+
" '<button class=\"quick-menu-btn\" onclick=\"document.dispatchEvent(new CustomEvent(\\'plugin-action\\', {detail:\\'rest\\'}))\">🏕️</button>' +",
|
|
333
|
+
" '</div>',",
|
|
334
|
+
' { priority: 1 }',
|
|
335
|
+
' );',
|
|
336
|
+
'}',
|
|
337
|
+
].join('\n');
|
|
338
|
+
|
|
339
|
+
export const CODE_DEBUG_EXAMPLE = [
|
|
340
|
+
'function setup(xinyu) {',
|
|
341
|
+
" console.log('插件 manifest:', xinyu.plugin.getManifest());",
|
|
342
|
+
" console.log('插件配置:', xinyu.plugin.config.get());",
|
|
343
|
+
'',
|
|
344
|
+
' // 查看游戏状态',
|
|
345
|
+
" console.log('游戏状态:', xinyu.game.getState());",
|
|
346
|
+
" console.log('游戏属性:', xinyu.game.getAttributes());",
|
|
347
|
+
'}',
|
|
348
|
+
].join('\n');
|
|
349
|
+
|
|
350
|
+
// ==================== 新增代码块常量 ====================
|
|
351
|
+
|
|
352
|
+
// --- CSS 变量章节 ---
|
|
353
|
+
export const CODE_CSS_VAR_INLINE = [
|
|
354
|
+
'// 在内联 style 中使用 CSS 变量',
|
|
355
|
+
"xinyu.ui.registerSlot('status-bar',",
|
|
356
|
+
" '<div style=\"color:var(--color-text-primary);background:var(--color-bg-secondary);",
|
|
357
|
+
" padding:4px 8px;border-radius:var(--border-radius);",
|
|
358
|
+
" border:1px solid var(--color-border)\">自定义状态栏</div>',",
|
|
359
|
+
' { priority: 1 }',
|
|
360
|
+
');',
|
|
361
|
+
'',
|
|
362
|
+
'// DOM 沙箱创建元素时使用',
|
|
363
|
+
"const el = xinyu.ui.dom.create('div', {",
|
|
364
|
+
" style: 'background:var(--color-bg-tertiary);color:var(--color-accent);",
|
|
365
|
+
" padding:8px;border-radius:var(--border-radius)'",
|
|
366
|
+
"}, '使用 CSS 变量的元素');",
|
|
367
|
+
].join('\n');
|
|
368
|
+
|
|
369
|
+
export const CODE_CSS_VAR_INJECT = [
|
|
370
|
+
'// 在 injectStyle 注入的 CSS 中使用 CSS 变量',
|
|
371
|
+
'xinyu.ui.injectStyle(`',
|
|
372
|
+
' .my-plugin-card {',
|
|
373
|
+
' background: var(--color-bg-secondary);',
|
|
374
|
+
' border: 1px solid var(--color-border);',
|
|
375
|
+
' border-radius: var(--border-radius);',
|
|
376
|
+
' padding: 16px;',
|
|
377
|
+
' box-shadow: 0 2px 8px var(--color-shadow);',
|
|
378
|
+
' }',
|
|
379
|
+
' .my-plugin-title {',
|
|
380
|
+
' color: var(--color-text-primary);',
|
|
381
|
+
' font-family: var(--font-heading);',
|
|
382
|
+
' font-size: 16px;',
|
|
383
|
+
' margin-bottom: 8px;',
|
|
384
|
+
' }',
|
|
385
|
+
' .my-plugin-desc {',
|
|
386
|
+
' color: var(--color-text-secondary);',
|
|
387
|
+
' font-family: var(--font-body);',
|
|
388
|
+
' font-size: 13px;',
|
|
389
|
+
' line-height: 1.6;',
|
|
390
|
+
' }',
|
|
391
|
+
' .my-plugin-btn {',
|
|
392
|
+
' background: var(--color-accent);',
|
|
393
|
+
' color: var(--color-text-primary);',
|
|
394
|
+
' border: none;',
|
|
395
|
+
' border-radius: var(--border-radius);',
|
|
396
|
+
' padding: 8px 16px;',
|
|
397
|
+
' cursor: pointer;',
|
|
398
|
+
' transition: background 0.2s;',
|
|
399
|
+
' }',
|
|
400
|
+
' .my-plugin-btn:hover {',
|
|
401
|
+
' background: var(--color-accent-hover);',
|
|
402
|
+
' }',
|
|
403
|
+
'`);',
|
|
404
|
+
].join('\n');
|
|
405
|
+
|
|
406
|
+
export const CODE_CSS_VAR_CUSTOM = [
|
|
407
|
+
'// 创建自定义 CSS 变量(建议使用插件前缀避免冲突)',
|
|
408
|
+
'xinyu.ui.injectStyle(`',
|
|
409
|
+
' :root {',
|
|
410
|
+
' --my-dice-primary: #e74c3c;',
|
|
411
|
+
' --my-dice-secondary: #c0392b;',
|
|
412
|
+
' --my-dice-bg: rgba(231, 76, 60, 0.1);',
|
|
413
|
+
' --my-dice-radius: 8px;',
|
|
414
|
+
' }',
|
|
415
|
+
' .dice-result {',
|
|
416
|
+
' background: var(--my-dice-bg);',
|
|
417
|
+
' color: var(--my-dice-primary);',
|
|
418
|
+
' border: 2px solid var(--my-dice-secondary);',
|
|
419
|
+
' border-radius: var(--my-dice-radius);',
|
|
420
|
+
' padding: 12px;',
|
|
421
|
+
' font-size: 24px;',
|
|
422
|
+
' font-weight: bold;',
|
|
423
|
+
' text-align: center;',
|
|
424
|
+
' }',
|
|
425
|
+
'`);',
|
|
426
|
+
'',
|
|
427
|
+
'// 使用自定义变量',
|
|
428
|
+
"xinyu.ui.registerSlot('status-bar',",
|
|
429
|
+
" '<div class=\"dice-result\">🎲 6</div>',",
|
|
430
|
+
' { priority: 5 }',
|
|
431
|
+
');',
|
|
432
|
+
].join('\n');
|
|
433
|
+
|
|
434
|
+
// --- 插件存储章节 ---
|
|
435
|
+
export const CODE_STORAGE_PLUGIN = [
|
|
436
|
+
'// 使用 xinyu.plugin.storage(推荐方式)',
|
|
437
|
+
'function setup(xinyu) {',
|
|
438
|
+
" // 读取用户偏好",
|
|
439
|
+
' const theme = xinyu.plugin.storage.get("theme") || "auto";',
|
|
440
|
+
' const fontSize = xinyu.plugin.storage.get("fontSize") || 14;',
|
|
441
|
+
'',
|
|
442
|
+
' // 保存用户偏好',
|
|
443
|
+
' xinyu.plugin.storage.set("theme", "dark");',
|
|
444
|
+
' xinyu.plugin.storage.set("fontSize", 16);',
|
|
445
|
+
'',
|
|
446
|
+
' // 也可以存储 JSON 对象(自动序列化)',
|
|
447
|
+
' xinyu.plugin.storage.set("userSettings", {',
|
|
448
|
+
' theme: "dark",',
|
|
449
|
+
' fontSize: 16,',
|
|
450
|
+
' language: "zh-CN",',
|
|
451
|
+
' notifications: true',
|
|
452
|
+
' });',
|
|
453
|
+
'',
|
|
454
|
+
' // 读取 JSON 对象',
|
|
455
|
+
' const settings = xinyu.plugin.storage.get("userSettings");',
|
|
456
|
+
' console.log(settings.theme); // "dark"',
|
|
457
|
+
'',
|
|
458
|
+
' // 删除某个键',
|
|
459
|
+
' xinyu.plugin.storage.remove("theme");',
|
|
460
|
+
'',
|
|
461
|
+
' // 获取所有键名',
|
|
462
|
+
' const allKeys = xinyu.plugin.storage.keys();',
|
|
463
|
+
" console.log('存储的键:', allKeys);",
|
|
464
|
+
'}',
|
|
465
|
+
].join('\n');
|
|
466
|
+
|
|
467
|
+
export const CODE_STORAGE_GAME = [
|
|
468
|
+
'// 使用 xinyu.game.setState / getState 管理游戏状态',
|
|
469
|
+
'function setup(xinyu) {',
|
|
470
|
+
" xinyu.plugin.on('onGameInit', function() {",
|
|
471
|
+
' // 初始化游戏状态',
|
|
472
|
+
' xinyu.game.setState({',
|
|
473
|
+
' gold: 100,',
|
|
474
|
+
' level: 1,',
|
|
475
|
+
' exp: 0,',
|
|
476
|
+
' currentScene: "village"',
|
|
477
|
+
' });',
|
|
478
|
+
' });',
|
|
479
|
+
'',
|
|
480
|
+
' // 读取游戏状态',
|
|
481
|
+
' const state = xinyu.game.getState();',
|
|
482
|
+
" console.log('金币:', state.gold);",
|
|
483
|
+
'',
|
|
484
|
+
' // 更新游戏状态(会触发 game:stateChange 事件)',
|
|
485
|
+
' xinyu.game.setState({ gold: state.gold + 50 });',
|
|
486
|
+
'',
|
|
487
|
+
' // 监听游戏状态变更(所有插件都能收到)',
|
|
488
|
+
" xinyu.ui.onHostEvent('game:stateChange', function(data) {",
|
|
489
|
+
" console.log('变更的键:', data.changedKeys);",
|
|
490
|
+
' // data.changedKeys = ["gold"]',
|
|
491
|
+
' });',
|
|
492
|
+
'}',
|
|
493
|
+
].join('\n');
|
|
494
|
+
|
|
495
|
+
export const CODE_STORAGE_ATTR = [
|
|
496
|
+
'// 使用 registerAttribute / setAttribute 管理游戏属性',
|
|
497
|
+
'function setup(xinyu) {',
|
|
498
|
+
" xinyu.plugin.on('onGameInit', function() {",
|
|
499
|
+
' // 注册属性(会显示在游戏属性面板中)',
|
|
500
|
+
' xinyu.game.registerAttribute({',
|
|
501
|
+
" key: 'hp',",
|
|
502
|
+
" label: '生命值',",
|
|
503
|
+
" type: 'number',",
|
|
504
|
+
" value: 100,",
|
|
505
|
+
" icon: '❤️',",
|
|
506
|
+
" color: '#e74c3c'",
|
|
507
|
+
' });',
|
|
508
|
+
'',
|
|
509
|
+
' xinyu.game.registerAttribute({',
|
|
510
|
+
" key: 'mp',",
|
|
511
|
+
" label: '魔法值',",
|
|
512
|
+
" type: 'number',",
|
|
513
|
+
" value: 50,",
|
|
514
|
+
" icon: '💎',",
|
|
515
|
+
" color: '#3498db'",
|
|
516
|
+
' });',
|
|
517
|
+
'',
|
|
518
|
+
' xinyu.game.registerAttribute({',
|
|
519
|
+
" key: 'status',",
|
|
520
|
+
" label: '状态',",
|
|
521
|
+
" type: 'string',",
|
|
522
|
+
" value: '正常',",
|
|
523
|
+
" icon: '⭐'",
|
|
524
|
+
' });',
|
|
525
|
+
' });',
|
|
526
|
+
'',
|
|
527
|
+
' // 更新属性值',
|
|
528
|
+
' xinyu.game.setAttribute("hp", 80);',
|
|
529
|
+
'',
|
|
530
|
+
' // 读取所有属性',
|
|
531
|
+
' const attrs = xinyu.game.getAttributes();',
|
|
532
|
+
" console.log('HP:', attrs.hp.value); // 80",
|
|
533
|
+
'}',
|
|
534
|
+
].join('\n');
|
|
535
|
+
|
|
536
|
+
// --- AI 拦截章节 ---
|
|
537
|
+
export const CODE_AI_PROMPT = [
|
|
538
|
+
'// 四种 AI 拦截器示例',
|
|
539
|
+
'function setup(xinyu) {',
|
|
540
|
+
'',
|
|
541
|
+
' // 1. Prompt 构建拦截 - 注入 NPC 人格',
|
|
542
|
+
' xinyu.ai.onPromptBuild(function(prompt, worldSetting) {',
|
|
543
|
+
' return prompt + "\\n\\n[系统指令] 你现在扮演酒馆老板老王,说话粗犷豪爽。";',
|
|
544
|
+
' });',
|
|
545
|
+
'',
|
|
546
|
+
' // 2. 发送前拦截 - 修改消息内容',
|
|
547
|
+
' xinyu.ai.onBeforeSend(function(messages) {',
|
|
548
|
+
' // messages: [{ role: "user", content: "..." }, ...]',
|
|
549
|
+
' return messages; // 返回修改后的消息数组',
|
|
550
|
+
' });',
|
|
551
|
+
'',
|
|
552
|
+
' // 3. 接收后拦截 - 内容格式化',
|
|
553
|
+
' xinyu.ai.onAfterReceive(function(content) {',
|
|
554
|
+
' // 移除多余的空行',
|
|
555
|
+
' return content.replace(/\\n{3,}/g, "\\n\\n");',
|
|
556
|
+
' });',
|
|
557
|
+
'',
|
|
558
|
+
' // 4. 请求配置拦截 - 动态调整模型参数',
|
|
559
|
+
' xinyu.ai.onRequestConfig(function(config) {',
|
|
560
|
+
' config.model = "gpt-4";',
|
|
561
|
+
' return config;',
|
|
562
|
+
' });',
|
|
563
|
+
'}',
|
|
564
|
+
].join('\n');
|
|
565
|
+
|
|
566
|
+
export const CODE_AI_PIPELINE = [
|
|
567
|
+
'// 管道模式:多个插件的拦截器按注册顺序依次执行',
|
|
568
|
+
'//',
|
|
569
|
+
'// 假设有三个插件都注册了 onPromptBuild:',
|
|
570
|
+
'//',
|
|
571
|
+
'// 原始 Prompt:',
|
|
572
|
+
'// "你是一个奇幻世界的 AI 导游..."',
|
|
573
|
+
'//',
|
|
574
|
+
'// Plugin A (NPC 人格插件) onPromptBuild:',
|
|
575
|
+
'// 输入: "你是一个奇幻世界的 AI 导游..."',
|
|
576
|
+
'// 输出: "你是一个奇幻世界的 AI 导游...\\n[你现在是酒馆老板老王]"',
|
|
577
|
+
'//',
|
|
578
|
+
'// Plugin B (规则注入插件) onPromptBuild:',
|
|
579
|
+
'// 输入: "你是一个奇幻世界的 AI 导游...\\n[你现在是酒馆老板老王]"',
|
|
580
|
+
'// 输出: "你是一个奇幻世界的 AI 导游...\\n[你现在是酒馆老板老王]\\n[规则: 战斗判定使用 d20]"',
|
|
581
|
+
'//',
|
|
582
|
+
'// Plugin C (状态注入插件) onPromptBuild:',
|
|
583
|
+
'// 输入: "...\\n[规则: 战斗判定使用 d20]"',
|
|
584
|
+
'// 输出: "...\\n[规则: 战斗判定使用 d20]\\n[当前玩家HP: 80/100]"',
|
|
585
|
+
'//',
|
|
586
|
+
'// 最终发送给 AI 的 Prompt = Plugin C 的输出',
|
|
587
|
+
'//',
|
|
588
|
+
'// 注意:每个 handler 应该返回修改后的值,不要返回 undefined',
|
|
589
|
+
].join('\n');
|
|
590
|
+
|
|
591
|
+
export const CODE_AI_NPC = [
|
|
592
|
+
'// 完整示例:NPC 人格注入插件',
|
|
593
|
+
'function setup(xinyu) {',
|
|
594
|
+
' const npcData = {',
|
|
595
|
+
' name: "酒馆老板·老王",',
|
|
596
|
+
' personality: "粗犷豪爽,喜欢讲冒险故事,偶尔会推销自己的特酿麦酒",',
|
|
597
|
+
' speech: "说话带点江湖气,用词朴实,偶尔夹杂方言"',
|
|
598
|
+
' };',
|
|
599
|
+
'',
|
|
600
|
+
' // 在 Prompt 中注入 NPC 人格',
|
|
601
|
+
' xinyu.ai.onPromptBuild(function(prompt) {',
|
|
602
|
+
' const npcPrompt = "\\n\\n[角色扮演指令]\\n"',
|
|
603
|
+
' + "你现在是 " + npcData.name + "\\n"',
|
|
604
|
+
' + "性格: " + npcData.personality + "\\n"',
|
|
605
|
+
' + "说话风格: " + npcData.speech + "\\n"',
|
|
606
|
+
' + "请始终保持角色,不要脱离人设。";',
|
|
607
|
+
' return prompt + npcPrompt;',
|
|
608
|
+
' });',
|
|
609
|
+
'',
|
|
610
|
+
' // 在状态栏显示当前 NPC',
|
|
611
|
+
" xinyu.ui.registerSlot('status-bar',",
|
|
612
|
+
' \'<div style="font-size:11px;color:var(--color-text-muted)">\' +',
|
|
613
|
+
" '🎭 当前 NPC: ' + npcData.name + '</div>',",
|
|
614
|
+
' { priority: 1 }',
|
|
615
|
+
' );',
|
|
616
|
+
'',
|
|
617
|
+
' // 注册切换 NPC 的按钮',
|
|
618
|
+
' xinyu.ui.registerInputToolbarButton({',
|
|
619
|
+
" id: 'switch-npc',",
|
|
620
|
+
" label: '切换NPC',",
|
|
621
|
+
" icon: '🎭',",
|
|
622
|
+
' order: 15,',
|
|
623
|
+
' onClick: function() {',
|
|
624
|
+
' xinyu.ui.showModal({',
|
|
625
|
+
" title: '🎭 切换 NPC',",
|
|
626
|
+
" content: '<div style=\"padding:12px\">选择要扮演的 NPC</div>',",
|
|
627
|
+
" width: '350px'",
|
|
628
|
+
' });',
|
|
629
|
+
' }',
|
|
630
|
+
' });',
|
|
631
|
+
'}',
|
|
632
|
+
].join('\n');
|
|
633
|
+
|
|
634
|
+
// --- 插件配置章节 ---
|
|
635
|
+
export const CODE_CONFIG_SCHEMA = [
|
|
636
|
+
'// configSchema 定义示例(骰子插件)',
|
|
637
|
+
'const configSchema = [',
|
|
638
|
+
' {',
|
|
639
|
+
" key: 'defaultDice',",
|
|
640
|
+
" label: '默认骰子面数',",
|
|
641
|
+
" type: 'select',",
|
|
642
|
+
" defaultValue: '20',",
|
|
643
|
+
" description: '掷骰时使用的默认面数',",
|
|
644
|
+
" options: [",
|
|
645
|
+
" { value: '4', label: 'D4 (四面骰)' },",
|
|
646
|
+
" { value: '6', label: 'D6 (六面骰)' },",
|
|
647
|
+
" { value: '8', label: 'D8 (八面骰)' },",
|
|
648
|
+
" { value: '10', label: 'D10 (十面骰)' },",
|
|
649
|
+
" { value: '12', label: 'D12 (十二面骰)' },",
|
|
650
|
+
" { value: '20', label: 'D20 (二十面骰)' },",
|
|
651
|
+
" { value: '100', label: 'D100 (百面骰)' }",
|
|
652
|
+
' ]',
|
|
653
|
+
' },',
|
|
654
|
+
' {',
|
|
655
|
+
" key: 'showAnimation',",
|
|
656
|
+
" label: '显示掷骰动画',",
|
|
657
|
+
" type: 'boolean',",
|
|
658
|
+
" defaultValue: true,",
|
|
659
|
+
" description: '掷骰时是否显示动画效果'",
|
|
660
|
+
' },',
|
|
661
|
+
' {',
|
|
662
|
+
" key: 'customModifier',",
|
|
663
|
+
" label: '固定修正值',",
|
|
664
|
+
" type: 'number',",
|
|
665
|
+
" defaultValue: 0,",
|
|
666
|
+
" description: '每次掷骰后自动加减的固定值'",
|
|
667
|
+
' },',
|
|
668
|
+
' {',
|
|
669
|
+
" key: 'rollCommand',",
|
|
670
|
+
" label: '掷骰指令前缀',",
|
|
671
|
+
" type: 'text',",
|
|
672
|
+
" defaultValue: '/roll',",
|
|
673
|
+
" description: '触发掷骰的聊天指令'",
|
|
674
|
+
' },',
|
|
675
|
+
' {',
|
|
676
|
+
" key: 'diceNote',",
|
|
677
|
+
" label: '骰子备注',",
|
|
678
|
+
" type: 'textarea',",
|
|
679
|
+
" defaultValue: '',",
|
|
680
|
+
" description: '自定义的骰子规则说明'",
|
|
681
|
+
' }',
|
|
682
|
+
'];',
|
|
683
|
+
].join('\n');
|
|
684
|
+
|
|
685
|
+
export const CODE_CONFIG_READ = [
|
|
686
|
+
'// 在插件代码中读取配置',
|
|
687
|
+
'function setup(xinyu) {',
|
|
688
|
+
'',
|
|
689
|
+
' // 获取全部配置',
|
|
690
|
+
' const config = xinyu.plugin.config.get();',
|
|
691
|
+
" console.log('默认骰子:', config.defaultDice);",
|
|
692
|
+
" console.log('显示动画:', config.showAnimation);",
|
|
693
|
+
'',
|
|
694
|
+
' // 获取单个配置值',
|
|
695
|
+
' const dice = xinyu.plugin.config.get("defaultDice");',
|
|
696
|
+
' const modifier = xinyu.plugin.config.get("customModifier");',
|
|
697
|
+
'',
|
|
698
|
+
' // 配置值来自用户在插件管理页面中的设置',
|
|
699
|
+
' // 如果用户未修改,则使用 defaultValue',
|
|
700
|
+
' const total = xinyu.utils.rollDice("1d" + dice).total + modifier;',
|
|
701
|
+
" xinyu.ui.toast('掷骰结果: ' + total, 'info');",
|
|
702
|
+
'}',
|
|
703
|
+
].join('\n');
|
|
704
|
+
|
|
705
|
+
export const CODE_CONFIG_FULL = [
|
|
706
|
+
'// 完整示例:带配置的骰子插件',
|
|
707
|
+
'function setup(xinyu) {',
|
|
708
|
+
' // 读取配置',
|
|
709
|
+
' const config = xinyu.plugin.config.get();',
|
|
710
|
+
' const dice = config.defaultDice || "20";',
|
|
711
|
+
' const modifier = config.customModifier || 0;',
|
|
712
|
+
' const showAnim = config.showAnimation !== false;',
|
|
713
|
+
'',
|
|
714
|
+
' // 注册掷骰指令',
|
|
715
|
+
' xinyu.chat.registerCommand({',
|
|
716
|
+
" name: config.rollCommand || '/roll',",
|
|
717
|
+
" description: '掷骰子',",
|
|
718
|
+
' handler: function(args) {',
|
|
719
|
+
' const notation = args[0] || ("1d" + dice);',
|
|
720
|
+
' const result = xinyu.utils.rollDice(notation);',
|
|
721
|
+
' const total = result.total + modifier;',
|
|
722
|
+
'',
|
|
723
|
+
' const msg = "🎲 " + notation;',
|
|
724
|
+
' if (modifier !== 0) msg += (modifier > 0 ? "+" : "") + modifier;',
|
|
725
|
+
' msg += " = " + total;',
|
|
726
|
+
'',
|
|
727
|
+
' xinyu.chat.insertSystemMessage(msg);',
|
|
728
|
+
' return msg;',
|
|
729
|
+
' }',
|
|
730
|
+
' });',
|
|
731
|
+
'',
|
|
732
|
+
' // 在状态栏显示当前骰子设置',
|
|
733
|
+
" xinyu.ui.registerSlot('status-bar',",
|
|
734
|
+
' \'<div style="font-size:11px;color:var(--color-text-muted)">\' +',
|
|
735
|
+
" '🎲 D' + dice + (modifier ? (modifier > 0 ? '+' : '') + modifier : '') + '</div>',",
|
|
736
|
+
' { priority: 5 }',
|
|
737
|
+
' );',
|
|
738
|
+
'}',
|
|
739
|
+
].join('\n');
|
|
740
|
+
|
|
741
|
+
// --- 导入导出章节 ---
|
|
742
|
+
export const CODE_EXPORT_FORMAT = [
|
|
743
|
+
'// 导出格式示例',
|
|
744
|
+
'//',
|
|
745
|
+
'// 单个插件导出(直接返回插件 JSON):',
|
|
746
|
+
'{',
|
|
747
|
+
' "id": "my-dice-plugin",',
|
|
748
|
+
' "name": "我的骰子插件",',
|
|
749
|
+
' "version": "1.0.0",',
|
|
750
|
+
' "type": "game-mechanics",',
|
|
751
|
+
' "description": "一个简单的骰子插件",',
|
|
752
|
+
' "author": "作者名",',
|
|
753
|
+
' "code": "function setup(xinyu) { ... }",',
|
|
754
|
+
' "configSchema": [...],',
|
|
755
|
+
' "commonPermissions": ["slot:status-bar", "style:inject"],',
|
|
756
|
+
' "exclusivePermissions": [],',
|
|
757
|
+
' "requiredPermissions": []',
|
|
758
|
+
'}',
|
|
759
|
+
'',
|
|
760
|
+
'// 批量导出(xinyu-extension-v1 格式):',
|
|
761
|
+
'{',
|
|
762
|
+
' "format": "xinyu-extension-v1",',
|
|
763
|
+
' "exportedAt": "2026-04-26T12:00:00.000Z",',
|
|
764
|
+
' "plugins": [',
|
|
765
|
+
' { "id": "plugin-a", ... },',
|
|
766
|
+
' { "id": "plugin-b", ... }',
|
|
767
|
+
' ]',
|
|
768
|
+
'}',
|
|
769
|
+
].join('\n');
|
|
770
|
+
|
|
771
|
+
export const CODE_IMPORT_FORMAT = [
|
|
772
|
+
'// 导入格式(支持 3 种)',
|
|
773
|
+
'//',
|
|
774
|
+
'// 格式 1:完整导出格式',
|
|
775
|
+
'{',
|
|
776
|
+
' "format": "xinyu-extension-v1",',
|
|
777
|
+
' "exportedAt": "2026-04-26T12:00:00.000Z",',
|
|
778
|
+
' "plugins": [',
|
|
779
|
+
' {',
|
|
780
|
+
' "id": "my-plugin",',
|
|
781
|
+
' "name": "我的插件",',
|
|
782
|
+
' "version": "1.0.0",',
|
|
783
|
+
' "type": "game-mechanics",',
|
|
784
|
+
' "description": "插件描述",',
|
|
785
|
+
' "author": "作者名",',
|
|
786
|
+
' "code": "function setup(xinyu) { ... }",',
|
|
787
|
+
' "configSchema": [...],',
|
|
788
|
+
' "commonPermissions": [...],',
|
|
789
|
+
' "exclusivePermissions": [...],',
|
|
790
|
+
' "requiredPermissions": [...]',
|
|
791
|
+
' }',
|
|
792
|
+
' ]',
|
|
793
|
+
'}',
|
|
794
|
+
'',
|
|
795
|
+
'// 格式 2:直接数组',
|
|
796
|
+
'[',
|
|
797
|
+
' { "id": "plugin-a", "name": "插件A", "code": "..." },',
|
|
798
|
+
' { "id": "plugin-b", "name": "插件B", "code": "..." }',
|
|
799
|
+
']',
|
|
800
|
+
'',
|
|
801
|
+
'// 格式 3:单个插件',
|
|
802
|
+
'{',
|
|
803
|
+
' "id": "my-plugin",',
|
|
804
|
+
' "name": "我的插件",',
|
|
805
|
+
' "code": "function setup(xinyu) { ... }"',
|
|
806
|
+
'}',
|
|
807
|
+
].join('\n');
|
|
808
|
+
|
|
809
|
+
// --- 安全章节 ---
|
|
810
|
+
export const CODE_SECURITY_TAGS = [
|
|
811
|
+
'// 允许的 HTML 标签完整列表(46 个)',
|
|
812
|
+
'//',
|
|
813
|
+
'// 文本结构:div, span, p, h1, h2, h3, h4, h5, h6,',
|
|
814
|
+
'// br, hr, pre, code, blockquote',
|
|
815
|
+
'//',
|
|
816
|
+
'// 列表:ul, ol, li, dl, dt, dd',
|
|
817
|
+
'//',
|
|
818
|
+
'// 表格:table, thead, tbody, tfoot, tr, th, td, caption, colgroup, col',
|
|
819
|
+
'//',
|
|
820
|
+
'// 表单(仅展示,无功能):label, fieldset, legend',
|
|
821
|
+
'//',
|
|
822
|
+
'// 媒体:img, figure, figcaption, video, audio, source, canvas',
|
|
823
|
+
'//',
|
|
824
|
+
'// 语义化:article, section, aside, header, footer, nav, main,',
|
|
825
|
+
'// details, summary, time, mark, abbr, small, strong, em, b, i,',
|
|
826
|
+
'// u, s, sub, sup, del, ins',
|
|
827
|
+
'//',
|
|
828
|
+
'// 禁止的标签:script, style, link, meta, iframe,',
|
|
829
|
+
'// object, embed, applet, form, input, select, textarea, button',
|
|
830
|
+
'//',
|
|
831
|
+
'// 禁止的属性前缀:on*, srcdoc, formaction, xlink:href',
|
|
832
|
+
'// 禁止的协议:javascript:',
|
|
833
|
+
].join('\n');
|