Web Extension Integration
CoreLib Web Console can be extended by plugin-side frontend bundles. A plugin can expose file entries, custom GUI/item/resource editors, field metadata, list schemas, local preview fallbacks, and locale text.
The current integration has two layers:
- Server-side declarative registration through
src/main/resources/web-console.yml. - Frontend IIFE extension built from
web-console/src/main.tsxintosrc/main/resources/web-extensions/*.jsand loaded dynamically by the Web Console.
Minimal structure
YourPlugin/
├── src/main/java/.../YourPlugin.java
├── src/main/resources/
│ ├── plugin.yml
│ ├── web-console.yml
│ └── web-extensions/
│ └── yourplugin-extension.js
└── web-console/
├── package.json
├── vite.config.ts
└── src/main.tsxRegister on enable:
@Override
public void onEnable() {
WebConsoleRegistry.registerFromYaml(this);
}Unregister on disable:
@Override
public void onDisable() {
WebConsoleRegistry.unregisterModule(this);
}web-console.yml
module:
tone: custom
icon: '<svg viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg"><path d="M8 8h22v22H8z" fill="none" stroke="currentColor"/></svg>'
files:
- path: "config.yml"
kind: CONFIG
- path: "gui/**/*.yml"
kind: GUI
editor: "yourplugin:gui"
- path: "items/**/*.yml"
kind: ITEM
editor: "yourplugin:item"
- path: "sets/**/*.yml"
kind: SET
editor: "yourplugin:set"
extensions:
- id: "yourplugin:editor"
resource: "web-extensions/yourplugin-extension.js"
nodes:
- path: "custom_templates"
type: dynamic_map
creatableChildren: trueSupported top-level sections:
| Section | Description |
|---|---|
module | Module display metadata: name, summary, tone, icon. |
files | Registered file entries. Each entry supports path, kind, title, comment, editor. |
extensions | Frontend extension scripts. Each entry supports id and resource. |
nodes | Structured config node metadata. Each entry supports path, type, label, comment, creatableChildren. |
comments | Legacy node-comment format kept for compatibility. |
Built-in kind values are CONFIG, GUI, ITEM, and SCRIPT. Other values are registered as custom resource kinds and can be handled by custom surfaces.
Common node types include text, number, boolean, list, stringList, numberList, actions, object, dynamic_map, enum:A,B,C, and dynamic_enum:path.
Vite configuration
Extensions should be built as IIFE bundles and externalize React and the CoreLib frontend API:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react({ jsxRuntime: 'classic' })],
build: {
outDir: '../src/main/resources/web-extensions',
emptyOutDir: false,
lib: {
entry: 'src/main.tsx',
name: 'YourPluginWebConsoleExtension',
formats: ['iife'],
fileName: () => 'yourplugin-extension.js'
},
rollupOptions: {
external: ['react', 'emaki-web-console'],
output: {
globals: {
react: 'React',
'emaki-web-console': 'EmakiWebConsole'
}
}
}
}
});Key requirements:
- Output to
src/main/resources/web-extensionsso the file is included in the plugin JAR. - Keep
emptyOutDir: falsewhen multiple resources may share the directory. - Use IIFE format because the Web Console loads the file through a
<script>tag. - Externalize
reactto globalReact. - Externalize
emaki-web-consoleto globalEmakiWebConsole.
Extension entry
import { registerModuleLocale, registerPluginConfig, registerPluginGuiEditor } from 'emaki-web-console';
const MODULE = 'YourPlugin';
registerModuleLocale(MODULE, 'en-US', {
'yourplugin.module.name': 'YourPlugin',
'yourplugin.module.summary': 'Custom config, GUI, and item editing',
'yourplugin.file.config.title': 'Main Config',
'yourplugin.file.config.comment': 'Main plugin configuration.',
'yourplugin.surface.gui': 'YourPlugin GUI'
});
registerPluginConfig({
moduleId: MODULE,
metaFields: [
['feature.enabled', 'Enabled', 'Controls whether this feature is enabled.', 'boolean'],
['feature.cooldown', 'Cooldown', 'Cooldown after triggering, in ticks.', 'number']
],
ruleFields: {
item_sources: ['Item Sources', 'Vanilla or external item source IDs.', 'stringList'],
actions: ['Actions', 'CoreLib actions executed in order.', 'actions']
}
});
registerPluginGuiEditor({
moduleId: MODULE,
editorId: 'yourplugin:gui',
label: 'YourPlugin GUI',
fields: [
['confirm', 'Confirm Button', 'Button slot that executes the main operation.', 'text'],
['preview', 'Preview Slot', 'Slot used to preview the current result.', 'text']
]
});Surface matching
Surface matching order:
- Exact
editorId. moduleId + kind.kindonly.
Higher priority wins when multiple registrations match.
import type { SurfaceProps } from 'emaki-web-console';
import { registerSurface } from 'emaki-web-console';
function SetEditorSurface(props: SurfaceProps) {
return <div>{props.module.name} / {props.file.path}</div>;
}
registerSurface({
kind: 'SET',
moduleId: 'YourPlugin',
editorId: 'yourplugin:set',
component: SetEditorSurface,
label: 'Set',
priority: 120
});SurfaceProps includes the current module, file, ApiClient, child path, editor descriptor, reload callback, and toolbar control callback.
Useful registration functions
| Function | Purpose |
|---|---|
registerModuleLocale | Register module-specific translations. |
registerSurface | Register a custom editor surface. |
registerPluginGuiEditor | Register a GUI editor using CoreLib's generic GUI surface. |
registerEditorDescriptor / registerEditorField | Add or override editor metadata and fields. |
registerPluginConfig | Batch-register config metadata, rules, templates, and list schemas. |
registerConfigNodeMeta / registerConfigNodeRule | Add exact or rule-based config node metadata. |
registerConfigCreateTemplate | Add create templates to dynamic config nodes. |
registerConfigListItemSchema | Add schemas for list items. |
registerFileKindLabel | Add a label for custom file kinds such as SET. |
registerItemFieldRenderer | Register a custom renderer for item editor fields. |
registerItemPreviewFallback | Register a local item preview fallback. |
registerSourceDocumentAdapter | Override source read/save/parse/serialize behavior. |
getRuntimeEnum | Read runtime enum values returned by the backend registry. |
ApiClient methods
Extensions can call props.api from a surface:
| Method | Purpose |
|---|---|
registry() | Reload the registry. |
registryFileNodes(moduleId, path) | Load structured nodes for a glob child file. |
saveRegistryValue(moduleId, filePath, path, value, revision) | Save a structured config node. |
createFile(moduleId, fileId, name, content?) | Create a child file under a glob entry. |
deleteFile(moduleId, fileId, path, confirmPath) | Delete a child file. |
readTextDocument() / saveTextDocument() | Read or save source by kind. |
readGui() / saveGui() | Read or save GUI YAML. |
readItem() / saveItem() | Read or save item YAML. |
readResource() / saveResource() | Read or save custom resource YAML. |
previewItem() | Request server-side item preview. |
actionTypes() | Load name/lore action types. |
economyProviders() | Load economy provider options. |
Write endpoints obey web_console.security.allow_config_write and use revision checks to avoid overwriting concurrent changes.
Built-in extension examples
| Module | Extension ID | Main features |
|---|---|---|
| EmakiAttribute | emakiattribute:locale | Attribute, damage, condition, lore-format metadata and runtime DamageCause enum usage. |
| EmakiForge | emakiforge:gui-surface | Quality config schemas, forge GUI editor, recipe and item adjustment metadata. |
| EmakiStrengthen | emakistrengthen:gui-surface | Strengthen config, recipes, and GUI editor. |
| EmakiCooking | emakicooking:gui-surface | Station config, display entity config, and cooking GUI editor. |
| EmakiGem | emakigem:item-surface | Gem/socket item editors, custom cost/effect/upgrade fields, local preview fallback, GUI editor. |
| EmakiSkills | emakiskills:gui-surface | Skill, resource, trigger, and GUI metadata. |
| EmakiItem | emakiitem:locale | SET kind, item editor, set editor, set field renderers, source document adapter. |
Debug checklist
- Build the extension with
npm run buildin the pluginweb-consoledirectory. - Confirm the generated script exists under
src/main/resources/web-extensions. - Build the plugin JAR and confirm it contains both
web-console.ymland the extension script. - Enable
web_console.enabled=trueand open the URL from/emakicorelib web. - If the module is missing, check plugin startup,
allowed_modules, andweb-console.ymlparsing warnings. - If the extension does not load, inspect
/api/registry, the/extensions/...jsrequest, and browser console errors. - Use
/emakicorelib webdebug frontendor/emakicorelib webdebug backendtemporarily while debugging.
CoreLib currently exposes frontend API version 1.1.0. Extension scripts should rely only on global React and EmakiWebConsole, and local previews should remain editing aids rather than substitutes for server-side logic.