@nilvn/core 0.14.0 → 0.15.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/dist/commands.js +39 -0
- package/dist/plugin-manifest.d.ts +38 -2
- package/dist/plugin-manifest.js +24 -0
- package/dist/versions.d.ts +1 -1
- package/dist/versions.js +1 -1
- package/package.json +1 -1
package/dist/commands.js
CHANGED
|
@@ -123,6 +123,26 @@ export const BUILTIN_COMMANDS = [
|
|
|
123
123
|
{ key: 'target', label: 'cmd.window.target', type: 'string', default: 'dialog', advanced: true },
|
|
124
124
|
],
|
|
125
125
|
},
|
|
126
|
+
{
|
|
127
|
+
name: 'theme',
|
|
128
|
+
label: 'cmd.theme.label',
|
|
129
|
+
category: 'stage',
|
|
130
|
+
icon: '🎨',
|
|
131
|
+
hint: 'cmd.theme.hint',
|
|
132
|
+
// The engine accepts ANY token the theme contract names (`--nilvn-<token>`);
|
|
133
|
+
// the form offers the everyday ones. `[theme reset]` clears the script layer.
|
|
134
|
+
params: [
|
|
135
|
+
{ key: 'dialog-bg', label: 'cmd.theme.dialogBg', type: 'string' },
|
|
136
|
+
{ key: 'dialog-opacity', label: 'cmd.theme.dialogOpacity', type: 'number' },
|
|
137
|
+
{ key: 'text-color', label: 'cmd.theme.textColor', type: 'string' },
|
|
138
|
+
{ key: 'text-size', label: 'cmd.theme.textSize', type: 'string', advanced: true },
|
|
139
|
+
{ key: 'name-bg', label: 'cmd.theme.nameBg', type: 'string', advanced: true },
|
|
140
|
+
{ key: 'name-color', label: 'cmd.theme.nameColor', type: 'string', advanced: true },
|
|
141
|
+
{ key: 'font', label: 'cmd.theme.font', type: 'string', advanced: true },
|
|
142
|
+
{ key: 'accent', label: 'cmd.theme.accent', type: 'string', advanced: true },
|
|
143
|
+
{ key: 'ui-scale', label: 'cmd.theme.uiScale', type: 'number', advanced: true },
|
|
144
|
+
],
|
|
145
|
+
},
|
|
126
146
|
{
|
|
127
147
|
name: 'textspeed',
|
|
128
148
|
label: 'cmd.textspeed.label',
|
|
@@ -174,6 +194,25 @@ export const BUILTIN_COMMANDS = [
|
|
|
174
194
|
hint: 'cmd.end.hint',
|
|
175
195
|
params: [{ key: 'sec', label: 'cmd.end.sec', type: 'number', default: 1, positional: 0 }],
|
|
176
196
|
},
|
|
197
|
+
{
|
|
198
|
+
name: 'ending',
|
|
199
|
+
label: 'cmd.ending.label',
|
|
200
|
+
category: 'flow',
|
|
201
|
+
icon: '🎬',
|
|
202
|
+
hint: 'cmd.ending.hint',
|
|
203
|
+
params: [
|
|
204
|
+
{ key: 'id', label: 'cmd.ending.id', type: 'string', default: 'default', positional: 0 },
|
|
205
|
+
{ key: 'sec', label: 'cmd.ending.sec', type: 'number', default: 1, advanced: true },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'title',
|
|
210
|
+
label: 'cmd.title.label',
|
|
211
|
+
category: 'flow',
|
|
212
|
+
icon: '🏠',
|
|
213
|
+
hint: 'cmd.title.hint',
|
|
214
|
+
params: [],
|
|
215
|
+
},
|
|
177
216
|
];
|
|
178
217
|
export const BUILTIN_COMMAND_MAP = Object.fromEntries(BUILTIN_COMMANDS.map((c) => [c.name, c]));
|
|
179
218
|
export function getCommandSchema(name) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommandSchema, EffectSchema, ObjectKindSchema, TextEffectDef } from './schema.js';
|
|
1
|
+
import type { CommandSchema, EffectSchema, ObjectKindSchema, ParamSchema, ParamType, TextEffectDef } from './schema.js';
|
|
2
2
|
/** The plugin↔host contract version. A manifest declaring a higher `apiVersion`
|
|
3
3
|
* than the host supports is rejected (forward-compat is not guaranteed). v1 was
|
|
4
4
|
* the pre-batch-B `{ name, entry, capabilities }` shape; v2 = this file. */
|
|
@@ -24,7 +24,7 @@ export interface PermissionDef {
|
|
|
24
24
|
/** The closed capability catalog. */
|
|
25
25
|
export declare const PERMISSIONS: readonly PermissionDef[];
|
|
26
26
|
/** A permission id as a manifest writes it (pattern ids carry their suffix). */
|
|
27
|
-
export type Permission = 'stage.read' | 'stage.write' | `stage.layer:${string}` | 'audio.play' | `audio.bus:${string}` | 'audio.capture' | 'vars.read' | 'vars.write' | 'save.slice' | 'session.save' | 'session.settings' | 'session.backlog' | 'session.replay' | 'ui.layer' | 'ui.panel' | 'ui.window' | 'ui.toast' | 'ui.inspector' | 'project.read' | 'project.commit' | 'assets.read' | 'assets.write' | 'fs.pick' | 'fs.project' | `net:${string}` | 'clipboard' | 'timer' | 'ai.text' | 'ai.image' | 'ai.audio' | 'ai.code' | 'ai.context';
|
|
27
|
+
export type Permission = 'stage.read' | 'stage.write' | `stage.layer:${string}` | 'audio.play' | `audio.bus:${string}` | 'audio.capture' | 'vars.read' | 'vars.write' | 'save.slice' | 'session.save' | 'session.settings' | 'session.backlog' | 'session.replay' | 'ui.layer' | 'ui.screen' | 'ui.dialog' | 'storage.local' | 'ui.panel' | 'ui.window' | 'ui.toast' | 'ui.inspector' | 'project.read' | 'project.commit' | 'assets.read' | 'assets.write' | 'fs.pick' | 'fs.project' | `net:${string}` | 'clipboard' | 'timer' | 'ai.text' | 'ai.image' | 'ai.audio' | 'ai.code' | 'ai.context';
|
|
28
28
|
/** Resolve a permission id (exact, or a pattern id with its suffix) to its
|
|
29
29
|
* catalog entry; undefined for anything outside the catalog. */
|
|
30
30
|
export declare function matchPermission(id: string): PermissionDef | undefined;
|
|
@@ -46,6 +46,37 @@ export interface PanelDef {
|
|
|
46
46
|
/** Free-form descriptor; the plugin renders its own panel body. */
|
|
47
47
|
kind?: string;
|
|
48
48
|
}
|
|
49
|
+
/** One plugin setting (`contributes.config`): a `ParamSchema` minus the command
|
|
50
|
+
* positional / required bits, plus a numeric range and who may change it. */
|
|
51
|
+
export interface ConfigFieldSchema extends Omit<ParamSchema, 'positional' | 'required'> {
|
|
52
|
+
min?: number;
|
|
53
|
+
max?: number;
|
|
54
|
+
step?: number;
|
|
55
|
+
/** `author` (default): set in the config file / the studio, read-only in the
|
|
56
|
+
* game. `player`: also a row in the in-game settings panel, persisted per work
|
|
57
|
+
* (the author's value is the default). */
|
|
58
|
+
scope?: 'author' | 'player';
|
|
59
|
+
}
|
|
60
|
+
/** An entry a plugin adds to the in-game system menu or the title page. */
|
|
61
|
+
export interface MenuItemDef {
|
|
62
|
+
id: string;
|
|
63
|
+
/** An i18n id resolved through the plugin's `messages`. */
|
|
64
|
+
label: string;
|
|
65
|
+
/** Menu entries: show only while `playing` (default) or on every session state. */
|
|
66
|
+
when?: 'playing' | 'always';
|
|
67
|
+
}
|
|
68
|
+
/** A HUD widget slot. */
|
|
69
|
+
export interface HudDef {
|
|
70
|
+
id: string;
|
|
71
|
+
slot?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
72
|
+
}
|
|
73
|
+
/** A field an actor declaration may carry for this plugin. */
|
|
74
|
+
export interface ActorFieldDef {
|
|
75
|
+
key: string;
|
|
76
|
+
type: ParamType;
|
|
77
|
+
/** An i18n id (the studio's actor form). */
|
|
78
|
+
label?: string;
|
|
79
|
+
}
|
|
49
80
|
/** Contributions per extension point. Every field optional; unknown keys are
|
|
50
81
|
* tolerated (ignored with a warning) so a newer plugin loads on an older host. */
|
|
51
82
|
export interface PluginContributions {
|
|
@@ -57,6 +88,11 @@ export interface PluginContributions {
|
|
|
57
88
|
effects?: EffectSchema[];
|
|
58
89
|
hooks?: string[];
|
|
59
90
|
saveSlice?: boolean;
|
|
91
|
+
config?: ConfigFieldSchema[];
|
|
92
|
+
menuItems?: MenuItemDef[];
|
|
93
|
+
titleItems?: MenuItemDef[];
|
|
94
|
+
hud?: HudDef[];
|
|
95
|
+
actorFields?: ActorFieldDef[];
|
|
60
96
|
panels?: PanelDef[];
|
|
61
97
|
nodeKinds?: string[];
|
|
62
98
|
stageTools?: string[];
|
package/dist/plugin-manifest.js
CHANGED
|
@@ -40,6 +40,8 @@ export const PERMISSIONS = [
|
|
|
40
40
|
{ id: 'session.backlog', group: 'session', side: 'engine', status: 'active', description: 'The dialogue backlog and voice replay by ref.' },
|
|
41
41
|
{ id: 'session.replay', group: 'session', side: 'engine', status: 'active', description: 'A–B replay segments: list / play / end / seen signals.' },
|
|
42
42
|
{ id: 'ui.layer', group: 'ui', side: 'engine', status: 'active', description: 'A host container inside the stage root (removed on dispose).' },
|
|
43
|
+
{ id: 'ui.screen', group: 'ui', side: 'engine', status: 'active', description: 'Full-stage screens over the story, entries in the system menu and on the title page, HUD widgets (the menuItems / titleItems / hud contributions).' },
|
|
44
|
+
{ id: 'ui.dialog', group: 'ui', side: 'engine', status: 'active', description: 'In-engine confirm / alert boxes and toasts (never the browser’s).' },
|
|
43
45
|
{ id: 'ui.panel', group: 'ui', side: 'editor', status: 'active', description: 'A manager panel host in the editor.' },
|
|
44
46
|
{ id: 'ui.window', group: 'ui', side: 'editor', status: 'active', description: 'A draggable editor window.' },
|
|
45
47
|
{ id: 'ui.toast', group: 'ui', side: 'editor', status: 'active', description: 'Editor toasts.' },
|
|
@@ -53,6 +55,7 @@ export const PERMISSIONS = [
|
|
|
53
55
|
{ id: 'net:', group: 'system', side: 'host', status: 'reserved', pattern: true, description: 'Network access to one origin (host-mediated; third-party default-deny).' },
|
|
54
56
|
{ id: 'clipboard', group: 'system', side: 'host', status: 'reserved', description: 'Clipboard read / write.' },
|
|
55
57
|
{ id: 'timer', group: 'system', side: 'engine', status: 'active', description: 'setTimeout / setInterval / requestAnimationFrame, cleared on dispose.' },
|
|
58
|
+
{ id: 'storage.local', group: 'data', side: 'engine', status: 'active', description: 'A key-value store namespaced per work and plugin (async, JSON values) — the engine’s SaveStore.' },
|
|
56
59
|
{ id: 'ai.text', group: 'ai', side: 'host', status: 'reserved', description: 'Text generation through the configured provider.' },
|
|
57
60
|
{ id: 'ai.image', group: 'ai', side: 'host', status: 'reserved', description: 'Image generation.' },
|
|
58
61
|
{ id: 'ai.audio', group: 'ai', side: 'host', status: 'reserved', description: 'Audio / voice generation.' },
|
|
@@ -76,6 +79,11 @@ export const EXTENSION_POINTS = [
|
|
|
76
79
|
{ key: 'effects', side: 'engine', status: 'active', version: 1, description: 'Retargetable effects bound to kinds via appliesToKinds.' },
|
|
77
80
|
{ key: 'hooks', side: 'engine', status: 'active', version: 1, description: 'Engine hook names the runtime half listens to (introspection; the module is authoritative).' },
|
|
78
81
|
{ key: 'saveSlice', side: 'engine', status: 'active', version: 1, description: 'Declares a SaveState.ext slice owned by this plugin.' },
|
|
82
|
+
{ key: 'config', side: 'engine', status: 'active', version: 1, description: 'Plugin settings (ConfigFieldSchema[]): the config file’s [plugins.<id>] table and ctx.config at runtime; scope = player rows also appear in the in-game settings panel and persist per work.' },
|
|
83
|
+
{ key: 'menuItems', side: 'engine', status: 'active', version: 1, description: 'Entries in the in-game system menu (wired with ctx.screen.menuItem; needs ui.screen).' },
|
|
84
|
+
{ key: 'titleItems', side: 'engine', status: 'active', version: 1, description: 'Buttons on the title page (ctx.screen.titleItem; needs ui.screen).' },
|
|
85
|
+
{ key: 'hud', side: 'engine', status: 'active', version: 1, description: 'Persistent widgets in a stage corner while playing (ctx.screen.hud; needs ui.screen).' },
|
|
86
|
+
{ key: 'actorFields', side: 'engine', status: 'active', version: 1, description: 'Fields an actor declaration may carry for this plugin ([actors.<id>] / [actor …]), reached as ctx.actorField(actorId, key).' },
|
|
79
87
|
{ key: 'rendererLayers', side: 'engine', status: 'reserved', version: 1, description: 'Named renderer layers.' },
|
|
80
88
|
{ key: 'panels', side: 'editor', status: 'active', version: 1, description: 'Manager panels (the editor renders the host; the plugin fills it).' },
|
|
81
89
|
{ key: 'nodeKinds', side: 'editor', status: 'active', version: 1, description: 'IR node kinds this plugin owns (forms + inert flag while disabled).' },
|
|
@@ -143,6 +151,22 @@ export function validatePluginManifest(m, opts = {}) {
|
|
|
143
151
|
else if (EXTENSION_POINTS.find((p) => p.key === key).status === 'reserved')
|
|
144
152
|
warnings.push(`extension point "${key}" is reserved — ignored`);
|
|
145
153
|
}
|
|
154
|
+
const CONFIG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
for (const f of m.contributes?.config ?? []) {
|
|
157
|
+
if (!f || typeof f.key !== 'string' || !CONFIG_KEY_RE.test(f.key))
|
|
158
|
+
errors.push(`contributes.config: invalid key "${String(f?.key)}"`);
|
|
159
|
+
else if (seen.has(f.key))
|
|
160
|
+
errors.push(`contributes.config: duplicate key "${f.key}"`);
|
|
161
|
+
else
|
|
162
|
+
seen.add(f.key);
|
|
163
|
+
if (f && f.scope !== undefined && f.scope !== 'author' && f.scope !== 'player')
|
|
164
|
+
errors.push(`contributes.config."${String(f.key)}": unknown scope "${String(f.scope)}"`);
|
|
165
|
+
}
|
|
166
|
+
for (const a of m.contributes?.actorFields ?? []) {
|
|
167
|
+
if (!a || typeof a.key !== 'string' || !CONFIG_KEY_RE.test(a.key))
|
|
168
|
+
errors.push(`contributes.actorFields: invalid key "${String(a?.key)}"`);
|
|
169
|
+
}
|
|
146
170
|
if (m.activation?.engine && !['eager', 'onCommand', 'manual'].includes(m.activation.engine))
|
|
147
171
|
errors.push(`unknown activation.engine "${m.activation.engine}"`);
|
|
148
172
|
if (m.reload && m.reload !== 'hot' && m.reload !== 'restart')
|
package/dist/versions.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const FORMAT_VERSIONS: Readonly<{
|
|
|
8
8
|
package: 1;
|
|
9
9
|
/** Engine `SaveState.v` — `restoreState` returns false for any other value. */
|
|
10
10
|
saveState: 2;
|
|
11
|
-
/** The
|
|
11
|
+
/** The engine's save-slot wrapper (`SlotPayload.v` — the menu and autosave). */
|
|
12
12
|
saveSlot: 1;
|
|
13
13
|
/** The plugin manifest / runtime contract (`PluginManifest.apiVersion`) —
|
|
14
14
|
* `validatePluginManifest` rejects a newer one. */
|
package/dist/versions.js
CHANGED
|
@@ -17,7 +17,7 @@ export const FORMAT_VERSIONS = Object.freeze({
|
|
|
17
17
|
package: PACKAGE_FORMAT,
|
|
18
18
|
/** Engine `SaveState.v` — `restoreState` returns false for any other value. */
|
|
19
19
|
saveState: 2,
|
|
20
|
-
/** The
|
|
20
|
+
/** The engine's save-slot wrapper (`SlotPayload.v` — the menu and autosave). */
|
|
21
21
|
saveSlot: 1,
|
|
22
22
|
/** The plugin manifest / runtime contract (`PluginManifest.apiVersion`) —
|
|
23
23
|
* `validatePluginManifest` rejects a newer one. */
|
package/package.json
CHANGED