narraleaf-studio 0.9.0 → 1.1.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/README.md CHANGED
@@ -1,119 +1,119 @@
1
- # narraleaf-studio
2
-
3
- TypeScript declarations for the [NarraLeaf Studio](https://github.com/NarraLeaf/NarraLeaf-Studio)
4
- plugin API.
5
-
6
- This package contains **types only**. Studio itself supplies the implementation
7
- at load time through an import map — there is no runtime code here.
8
-
9
- ```bash
10
- yarn add -D narraleaf-studio
11
- ```
12
-
13
- ## Usage
14
-
15
- ```ts
16
- // Studio entry — runs in the editor
17
- import { definePlugin } from "narraleaf-studio/plugin";
18
-
19
- export default definePlugin({
20
- setup(app) {
21
- app.services.blueprintNodes.registerMany(myNodes());
22
- app.services.ui.notifications.info(`${app.manifest.name} loaded`);
23
- },
24
- });
25
- ```
26
-
27
- ```ts
28
- // Runtime entry — runs inside the game
29
- import { defineRuntimePlugin } from "narraleaf-studio/runtime";
30
-
31
- export default defineRuntimePlugin({
32
- setup(app) {
33
- app.game.blueprintNodes.registerMany(myNodes());
34
- },
35
- });
36
- ```
37
-
38
- | Subpath | Surface |
39
- | -------------------------- | ---------------------------------------------------------------- |
40
- | `narraleaf-studio/plugin` | Editor: `definePlugin`, `PluginApp`, `PluginServices`, `ui` |
41
- | `narraleaf-studio/runtime` | Game: `defineRuntimePlugin`, `RuntimePluginApp` |
42
-
43
- The two are physically isolated at runtime. Importing `narraleaf-studio/plugin`
44
- from a runtime entry throws — the runtime host has no Studio services.
45
-
46
- ## Tests
47
-
48
- `narraleaf-studio/plugin` also carries the **test protocol** (`TestDefinition` and
49
- everything it references, plus `TEST_PROTOCOL_VERSION`): checks an author runs
50
- against their own game from Studio's Run ▸ Test, contributed through
51
- `app.services.tests`. Nothing to do with your plugin's own unit tests.
52
-
53
- ```ts
54
- app.services.tests.register({
55
- id: "acme.qa-pack.scene-names", // declare it in contributes.tests
56
- title: { text: "Scenes are named" },
57
- presentation: "headless",
58
- requires: ["project.read"],
59
- async run(ctx) { /* … */ return { status: "passed" }; },
60
- });
61
- ```
62
-
63
- The contract — declaration, capabilities, who may claim which verdict, the
64
- game-session lifecycle — is
65
- [`docs/plugin-test-protocol.md`](https://github.com/NarraLeaf/NarraLeaf-Studio/blob/master/docs/plugin-test-protocol.md).
66
-
67
- `TEST_PROTOCOL_VERSION` is a compile-time constant; import it with `import type`
68
- and read the host's own version from `app.services.tests.protocolVersion`.
69
-
70
- ## You must mark these external
71
-
72
- Both specifiers have to be `external` in your bundler. Studio resolves them (and
73
- `react`, `react-dom`) through an import map, so bundling them produces a second,
74
- broken React instance and a plugin that fails to load.
75
-
76
- ```js
77
- // esbuild
78
- external: [
79
- "narraleaf-studio/plugin",
80
- "narraleaf-studio/runtime",
81
- "react",
82
- "react-dom",
83
- "react-dom/client",
84
- "react/jsx-runtime",
85
- ]
86
- ```
87
-
88
- If you forget, this package's stub throws at import with a message saying so.
89
-
90
- ## How these types are produced
91
-
92
- Generated directly from Studio's source by `packages/plugin-types/build.mjs`,
93
- not written by hand:
94
-
95
- | Output | Generated from |
96
- | ----------------- | ---------------------------------------------------------------- |
97
- | `dist/plugin.d.ts` | `src/renderer/plugin/index.ts` |
98
- | `dist/runtime.d.ts` | `src/renderer/lib/ui-editor/runtime/plugins/runtimePluginApi.ts` |
99
-
100
- Only what those entry points explicitly export is public. The build fails if the
101
- generated declarations do not typecheck on their own, so a release cannot ship a
102
- bundle that does not compile.
103
-
104
- ## Versioning
105
-
106
- This package versions independently of Studio: it changes when the plugin API
107
- changes, which is far less often than the app ships. Studio does not yet enforce
108
- a compatibility range at install time, so treat a major bump as "recompile your
109
- plugin and check it still typechecks".
110
-
111
- ## Getting started
112
-
113
- See the plugin authoring guide in the
114
- [NarraLeaf/Plugins](https://github.com/NarraLeaf/Plugins) registry, which has a
115
- working starter template.
116
-
117
- ## License
118
-
119
- MPL-2.0
1
+ # narraleaf-studio
2
+
3
+ TypeScript declarations for the [NarraLeaf Studio](https://github.com/NarraLeaf/NarraLeaf-Studio)
4
+ plugin API.
5
+
6
+ This package contains **types only**. Studio itself supplies the implementation
7
+ at load time through an import map — there is no runtime code here.
8
+
9
+ ```bash
10
+ yarn add -D narraleaf-studio
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ // Studio entry — runs in the editor
17
+ import { definePlugin } from "narraleaf-studio/plugin";
18
+
19
+ export default definePlugin({
20
+ setup(app) {
21
+ app.services.blueprintNodes.registerMany(myNodes());
22
+ app.services.ui.notifications.info(`${app.manifest.name} loaded`);
23
+ },
24
+ });
25
+ ```
26
+
27
+ ```ts
28
+ // Runtime entry — runs inside the game
29
+ import { defineRuntimePlugin } from "narraleaf-studio/runtime";
30
+
31
+ export default defineRuntimePlugin({
32
+ setup(app) {
33
+ app.game.blueprintNodes.registerMany(myNodes());
34
+ },
35
+ });
36
+ ```
37
+
38
+ | Subpath | Surface |
39
+ | -------------------------- | ---------------------------------------------------------------- |
40
+ | `narraleaf-studio/plugin` | Editor: `definePlugin`, `PluginApp`, `PluginServices`, `ui` |
41
+ | `narraleaf-studio/runtime` | Game: `defineRuntimePlugin`, `RuntimePluginApp` |
42
+
43
+ The two are physically isolated at runtime. Importing `narraleaf-studio/plugin`
44
+ from a runtime entry throws — the runtime host has no Studio services.
45
+
46
+ ## Tests
47
+
48
+ `narraleaf-studio/plugin` also carries the **test protocol** (`TestDefinition` and
49
+ everything it references, plus `TEST_PROTOCOL_VERSION`): checks an author runs
50
+ against their own game from Studio's Run ▸ Test, contributed through
51
+ `app.services.tests`. Nothing to do with your plugin's own unit tests.
52
+
53
+ ```ts
54
+ app.services.tests.register({
55
+ id: "acme.qa-pack.scene-names", // declare it in contributes.tests
56
+ title: { text: "Scenes are named" },
57
+ presentation: "headless",
58
+ requires: ["project.read"],
59
+ async run(ctx) { /* … */ return { status: "passed" }; },
60
+ });
61
+ ```
62
+
63
+ The contract — declaration, capabilities, who may claim which verdict, the
64
+ game-session lifecycle — is
65
+ [`docs/plugin-test-protocol.md`](https://github.com/NarraLeaf/NarraLeaf-Studio/blob/master/docs/plugin-test-protocol.md).
66
+
67
+ `TEST_PROTOCOL_VERSION` is a compile-time constant; import it with `import type`
68
+ and read the host's own version from `app.services.tests.protocolVersion`.
69
+
70
+ ## You must mark these external
71
+
72
+ Both specifiers have to be `external` in your bundler. Studio resolves them (and
73
+ `react`, `react-dom`) through an import map, so bundling them produces a second,
74
+ broken React instance and a plugin that fails to load.
75
+
76
+ ```js
77
+ // esbuild
78
+ external: [
79
+ "narraleaf-studio/plugin",
80
+ "narraleaf-studio/runtime",
81
+ "react",
82
+ "react-dom",
83
+ "react-dom/client",
84
+ "react/jsx-runtime",
85
+ ]
86
+ ```
87
+
88
+ If you forget, this package's stub throws at import with a message saying so.
89
+
90
+ ## How these types are produced
91
+
92
+ Generated directly from Studio's source by `packages/plugin-types/build.mjs`,
93
+ not written by hand:
94
+
95
+ | Output | Generated from |
96
+ | ----------------- | ---------------------------------------------------------------- |
97
+ | `dist/plugin.d.ts` | `src/renderer/plugin/index.ts` |
98
+ | `dist/runtime.d.ts` | `src/renderer/lib/ui-editor/runtime/plugins/runtimePluginApi.ts` |
99
+
100
+ Only what those entry points explicitly export is public. The build fails if the
101
+ generated declarations do not typecheck on their own, so a release cannot ship a
102
+ bundle that does not compile.
103
+
104
+ ## Versioning
105
+
106
+ This package versions independently of Studio: it changes when the plugin API
107
+ changes, which is far less often than the app ships. Studio does not yet enforce
108
+ a compatibility range at install time, so treat a major bump as "recompile your
109
+ plugin and check it still typechecks".
110
+
111
+ ## Getting started
112
+
113
+ See the plugin authoring guide in the
114
+ [NarraLeaf/Plugins](https://github.com/NarraLeaf/Plugins) registry, which has a
115
+ working starter template.
116
+
117
+ ## License
118
+
119
+ MPL-2.0
package/dist/_api.d.ts CHANGED
@@ -674,6 +674,11 @@ type StoryActionPayload = {
674
674
  * arm's - it builds the clip, names it and reveals it. Revealing is still not playing: the
675
675
  * element is on screen holding its first frame, and `/play` is what runs it.
676
676
  *
677
+ * A `play` carrying {@link assetId} is the one-row cutscene: it builds the clip, names it,
678
+ * reveals it and runs it to the end. Without one, `play` runs a clip an earlier row put on
679
+ * stage and leaves its visibility alone. No document needs migrating for this: a `play` row
680
+ * could always hold an asset, and one that did now does what it says.
681
+ *
677
682
  * Additive: the four transport operations and `timeMs` are new in A3, and no document
678
683
  * written before them carries either, so no schema bump.
679
684
  */
@@ -2109,6 +2114,21 @@ type PluginContributes = {
2109
2114
  * capability to consent to at install time.
2110
2115
  */
2111
2116
  tests?: string[];
2117
+ /**
2118
+ * Save ids the plugin keeps for its own bookkeeping - a quick-save slot, a checkpoint - rather
2119
+ * than slots a player chose.
2120
+ *
2121
+ * The game leaves them out of `List Saves`, and so out of every save screen built on it, the way
2122
+ * it leaves out its own autosave ring. Nothing else changes for them: the plugin still writes,
2123
+ * loads and finds them through `app.game.saves` (whose `listIds` is the raw store listing and
2124
+ * still includes them), and the explicit-id save nodes - `Load Save`, `Delete Save`, the
2125
+ * `Get Save` readers - take them like any other id.
2126
+ *
2127
+ * Prefixed with the plugin id like the type lists above, so a plugin can only reserve ids in its
2128
+ * own namespace and can never hide a slot the author's own `Save Game` wrote. That is also why it
2129
+ * derives no install permission: it reaches nothing the plugin did not already own.
2130
+ */
2131
+ reservedSaveIds?: string[];
2112
2132
  /**
2113
2133
  * Capability domains the `runtime` entry may use. Each maps 1:1 onto a
2114
2134
  * namespace on `app.game`, and an undeclared domain is absent from that
@@ -2172,9 +2192,29 @@ type PluginContributes = {
2172
2192
  */
2173
2193
  network?: string[];
2174
2194
  };
2195
+ /**
2196
+ * The plugin's name and description in one editor language. Either may be left out, and the plain
2197
+ * manifest field is shown in its place.
2198
+ */
2199
+ export type PluginManifestLocalizedText = {
2200
+ name?: string;
2201
+ description?: string;
2202
+ };
2203
+ /**
2204
+ * `name` and `description` per editor locale, keyed by the exact code Studio's language setting
2205
+ * stores (`zh`, `ja`, or a code a language pack adds). There is no region matching: an entry under
2206
+ * `zh-CN` is never shown, because no Studio locale is called that.
2207
+ */
2208
+ export type PluginManifestLocalized = Record<string, PluginManifestLocalizedText>;
2175
2209
  export type PluginManifestV2 = Omit<PluginIdentity, "id" | "name" | "version"> & Required<Pick<PluginIdentity, "id" | "name" | "version">> & {
2176
2210
  manifestVersion: typeof PluginManifestVersion;
2177
2211
  description?: string;
2212
+ /**
2213
+ * Translations of `name` and `description`, shown wherever Studio names the plugin to an author
2214
+ * whose editor is set to that language. The plain fields stay the plugin's identity: command
2215
+ * lines, project files and built games name it by them.
2216
+ */
2217
+ localized?: PluginManifestLocalized;
2178
2218
  /**
2179
2219
  * Package-relative path to the thumbnail shown beside the plugin's name in
2180
2220
  * the Launcher list. Square, at most 512x512, and one of the extensions in
@@ -2565,6 +2605,21 @@ type UIDocument = {
2565
2605
  * `inputAction.ts`.
2566
2606
  */
2567
2607
  actions?: Record<string, UIInputActionDef>;
2608
+ /**
2609
+ * The page the game starts on, by id.
2610
+ *
2611
+ * Absent means the page carrying `MAIN_APP_SURFACE_ID` - the id a new document gives its first
2612
+ * page, and the only entry a document had before this field existed - so a document whose author
2613
+ * never moved the entry stays exactly as short as it was. Read only through `resolveEntrySurface`
2614
+ * (`entrySurface.ts`), which also says what a pointer that has lost its page falls back to.
2615
+ *
2616
+ * A pointer rather than an id swap: a page's id is what its blueprints are filed under and what
2617
+ * every `Go Page` names, so making another page the entry must not change which id it has.
2618
+ *
2619
+ * Carries no schema bump, for the reason `actions` and `structs` did not: absence already means a
2620
+ * defined answer, so a document written before this field reads correctly without one.
2621
+ */
2622
+ entrySurfaceId?: UISurfaceId;
2568
2623
  meta?: UIDocumentMeta;
2569
2624
  };
2570
2625
  type UIDocumentMeta = {
@@ -3006,6 +3061,7 @@ declare const en: {
3006
3061
  readonly welcome: "Open Welcome";
3007
3062
  readonly docs: "Documentation";
3008
3063
  readonly feedback: "Send Feedback";
3064
+ readonly exportLogs: "Export Logs\u2026";
3009
3065
  readonly about: "About {name}";
3010
3066
  };
3011
3067
  };
@@ -4228,6 +4284,10 @@ declare const en: {
4228
4284
  readonly one: "{count} more waiting";
4229
4285
  readonly other: "{count} more waiting";
4230
4286
  };
4287
+ readonly repeated: {
4288
+ readonly one: "{count} time";
4289
+ readonly other: "{count} times";
4290
+ };
4231
4291
  };
4232
4292
  readonly background: {
4233
4293
  readonly command: "Set background image\u2026";
@@ -4696,6 +4756,7 @@ declare const en: {
4696
4756
  readonly localizationFind: "Find in Translations";
4697
4757
  readonly voiceFind: "Find in Voice-over";
4698
4758
  readonly lintFind: "Find in Problems";
4759
+ readonly blueprintOverviewFind: "Find in Blueprint Overview";
4699
4760
  readonly run: {
4700
4761
  readonly stop: "Stop the Run";
4701
4762
  };
@@ -5335,6 +5396,7 @@ declare const en: {
5335
5396
  readonly scopedTo: "Actions for {name}";
5336
5397
  readonly addStarred: "Add to starred";
5337
5398
  readonly removeStarred: "Remove from starred";
5399
+ readonly starredEmpty: "Starred commands are listed first in the \u201C/\u201D menu.";
5338
5400
  };
5339
5401
  readonly music: {
5340
5402
  readonly missingAudio: "Missing audio";
@@ -5899,8 +5961,8 @@ declare const en: {
5899
5961
  readonly kindExpression: "Expression";
5900
5962
  readonly expressionPlaceholder: "gold >= 100 && !met";
5901
5963
  readonly expressionVariables: "In scope: {names}";
5902
- readonly opIsOn: "is on";
5903
- readonly opIsOff: "is off";
5964
+ readonly opIsTrue: "is true";
5965
+ readonly opIsFalse: "is false";
5904
5966
  readonly opEquals: "equals";
5905
5967
  readonly opNotEquals: "does not equal";
5906
5968
  readonly opGreaterThan: "is greater than";
@@ -6028,7 +6090,7 @@ declare const en: {
6028
6090
  };
6029
6091
  readonly play: {
6030
6092
  readonly label: "Play";
6031
- readonly detail: "Play a video";
6093
+ readonly detail: "Play a video on stage, or a video from the asset library";
6032
6094
  };
6033
6095
  readonly front: {
6034
6096
  readonly label: "Bring to Front";
@@ -7643,7 +7705,6 @@ declare const en: {
7643
7705
  readonly collapse: "Collapse side panel";
7644
7706
  };
7645
7707
  readonly canvas: {
7646
- readonly addLayer: "Add layer";
7647
7708
  readonly selectLayer: "Select a layer on the left.";
7648
7709
  readonly previewUnavailable: "Preview unavailable";
7649
7710
  readonly unknownNode: "Unknown node";
@@ -7877,6 +7938,45 @@ declare const en: {
7877
7938
  readonly other: "Already run by {count} layers";
7878
7939
  };
7879
7940
  };
7941
+ readonly layerTemplates: {
7942
+ readonly heading: "New layer";
7943
+ readonly description: "Start from a template or from a blank layer.";
7944
+ readonly listLabel: "Layer templates";
7945
+ readonly blank: {
7946
+ readonly title: "Blank layer";
7947
+ readonly description: "An empty canvas, or one of your own scripts.";
7948
+ };
7949
+ readonly library: {
7950
+ readonly title: "More templates";
7951
+ readonly description: "Browse every template for this blueprint by category.";
7952
+ };
7953
+ };
7954
+ readonly templateLibrary: {
7955
+ readonly title: "Template library";
7956
+ readonly open: "Add from template library";
7957
+ readonly all: "All";
7958
+ readonly categoriesLabel: "Categories";
7959
+ readonly search: "Search templates";
7960
+ readonly emptyFiltered: "No templates match.";
7961
+ readonly empty: "No templates suit this blueprint.";
7962
+ readonly add: "Add";
7963
+ readonly back: "Back";
7964
+ readonly openDetail: "Show details";
7965
+ readonly choicesHeading: "To choose after adding";
7966
+ readonly choicesNone: "Runs as it is once added.";
7967
+ readonly previewLabel: "Graph preview";
7968
+ readonly category: {
7969
+ readonly pageFlow: "Page flow";
7970
+ readonly navigation: "Navigation";
7971
+ readonly game: "Game";
7972
+ readonly settings: "Settings";
7973
+ readonly audio: "Audio";
7974
+ readonly motion: "Motion";
7975
+ readonly keys: "Keys";
7976
+ readonly window: "Window";
7977
+ readonly display: "Display";
7978
+ };
7979
+ };
7880
7980
  readonly owner: {
7881
7981
  readonly global: "Global";
7882
7982
  readonly storyAction: "Story Action";
@@ -7980,6 +8080,7 @@ declare const en: {
7980
8080
  readonly select: "Select element";
7981
8081
  readonly selectNamed: "Select element {name}";
7982
8082
  readonly unbound: "Unbound";
8083
+ readonly missing: "Missing element";
7983
8084
  };
7984
8085
  readonly pin: {
7985
8086
  readonly addInput: "Add input pin";
@@ -9046,9 +9147,43 @@ declare const en: {
9046
9147
  readonly getPlaytime: "Get Playtime";
9047
9148
  readonly getTotalPlaytime: "Get Total Playtime";
9048
9149
  };
9150
+ readonly nodeDescription: {
9151
+ readonly compareEqual: "True when A and B are the same value of the same type. The number 1 and the text \"1\" are not equal.";
9152
+ readonly compareNotEqual: "True when A and B differ in value or in type. The number 1 and the text \"1\" count as different.";
9153
+ readonly compareGreaterThan: "True when A is greater than B. Both sides are compared as numbers; a side that cannot be read as a number gives false.";
9154
+ readonly compareGreaterThanOrEqual: "True when A is greater than or equal to B. Both sides are compared as numbers; a side that cannot be read as a number gives false.";
9155
+ readonly compareLessThan: "True when A is less than B. Both sides are compared as numbers; a side that cannot be read as a number gives false.";
9156
+ readonly compareLessThanOrEqual: "True when A is less than or equal to B. Both sides are compared as numbers; a side that cannot be read as a number gives false.";
9157
+ readonly mathEqual: "True when A and B are the same number. Both sides are read as numbers first, so the text \"1\" equals the number 1.";
9158
+ readonly mathNotEqual: "True when A and B are different numbers. Both sides are read as numbers first, so the text \"1\" and the number 1 count as equal.";
9159
+ readonly flowReturn: "Stops the current run here. Nothing after it runs, including the remaining outputs of a Sequence. Gives back no value; to give one back, use Return Value, or Fn Return inside a function.";
9160
+ readonly returnValue: "Gives the value back as the result and ends the run. Used where a blueprint has to produce a value, such as a Blueprint Value.";
9161
+ readonly appBoot: "Runs once each time the game starts, after the first screen is showing, and does not hold that screen back. On Game Ready has already run by then. In Dev Mode it runs again after every reload.";
9162
+ readonly gameReady: "Runs when the game is ready, before any story starts and before the first screen shows; the screen waits for it to finish. Runs before App Boot, and can run again when a game is started or loaded.";
9163
+ readonly goBack: "Closes the current page and returns to the page beneath it. On the bottom page it does nothing.";
9164
+ };
9049
9165
  readonly live: {
9050
9166
  readonly nodeClaimed: "{name} is editing this node";
9051
9167
  };
9168
+ readonly overview: {
9169
+ readonly title: "Blueprint Overview";
9170
+ readonly count: {
9171
+ readonly one: "{count} blueprint";
9172
+ readonly other: "{count} blueprints";
9173
+ };
9174
+ readonly noBlueprints: "No blueprints";
9175
+ readonly search: "Search blueprints";
9176
+ readonly noMatches: "No blueprints match \u201C{query}\u201D.";
9177
+ readonly nodes: {
9178
+ readonly one: "{count} node";
9179
+ readonly other: "{count} nodes";
9180
+ };
9181
+ readonly group: {
9182
+ readonly project: "Project";
9183
+ readonly componentCaption: "Component";
9184
+ readonly storyCaption: "Story";
9185
+ };
9186
+ };
9052
9187
  };
9053
9188
  readonly characters: {
9054
9189
  readonly errors: {
@@ -9376,6 +9511,7 @@ declare const en: {
9376
9511
  readonly addCreate: "Create action";
9377
9512
  readonly removeAction: "Stop answering {name}";
9378
9513
  readonly answer: "Answer {name}";
9514
+ readonly reveal: "Show {name} in Input Actions";
9379
9515
  readonly bubble: "After it fires";
9380
9516
  readonly bubbleStop: "Intercept";
9381
9517
  readonly bubbleContinue: "Continue";
@@ -9402,9 +9538,25 @@ declare const en: {
9402
9538
  readonly uiElement: "UI Element";
9403
9539
  readonly linkedComponent: "Linked Component";
9404
9540
  };
9541
+ readonly interactionSound: {
9542
+ readonly title: "Sound";
9543
+ readonly hover: "Hover";
9544
+ readonly hoverTip: "Plays when the pointer moves onto the element, on the SFX track unless its options name another. Of nested elements with a hover sound, only the innermost one plays.";
9545
+ readonly click: "Click";
9546
+ readonly clickTip: "Plays when the element is clicked, on the SFX track unless its options name another. Of nested elements with a click sound, only the innermost one plays.";
9547
+ readonly none: "No sound";
9548
+ readonly options: "Playback options";
9549
+ readonly summary: "Volume {volume} \u00B7 {track}";
9550
+ readonly volume: "Volume";
9551
+ readonly track: "Track";
9552
+ readonly chooseHover: "Choose hover sound";
9553
+ readonly chooseClick: "Choose click sound";
9554
+ readonly missing: "Missing sound";
9555
+ };
9405
9556
  readonly linkedComponent: {
9406
9557
  readonly missing: "Missing component";
9407
9558
  readonly info: "Linked instance. Only its params and its position, size and rotation can be changed before unlinking.";
9559
+ readonly reveal: "Show in Component Library";
9408
9560
  };
9409
9561
  readonly componentParams: {
9410
9562
  readonly title: "Params";
@@ -9853,6 +10005,10 @@ declare const en: {
9853
10005
  readonly string: "String";
9854
10006
  readonly json: "JSON";
9855
10007
  };
10008
+ readonly value: {
10009
+ readonly true: "True";
10010
+ readonly false: "False";
10011
+ };
9856
10012
  readonly row: {
9857
10013
  readonly nameAria: "Variable name";
9858
10014
  readonly defaultPlaceholder: "default";
@@ -9932,6 +10088,7 @@ declare const en: {
9932
10088
  readonly duplicateSurface: "Duplicate {label}";
9933
10089
  readonly copySurface: "Copy {label}";
9934
10090
  readonly pasteSurface: "Paste interface";
10091
+ readonly setEntryPage: "Set as Entry Page";
9935
10092
  readonly deleteSurface: "Delete {label}";
9936
10093
  readonly surfaceActions: "{label} actions";
9937
10094
  readonly deleteConfirm: "Delete {label}?";
@@ -9949,6 +10106,7 @@ declare const en: {
9949
10106
  };
9950
10107
  readonly history: {
9951
10108
  readonly moveSurface: "move interface {name}";
10109
+ readonly setEntryPage: "set entry page {name}";
9952
10110
  };
9953
10111
  readonly inputActions: {
9954
10112
  readonly title: "Input Actions";
@@ -10067,6 +10225,8 @@ declare const en: {
10067
10225
  readonly layerFallback: "Layer";
10068
10226
  readonly openComponent: "Open component";
10069
10227
  readonly unlinkComponent: "Unlink component";
10228
+ readonly openComponentNamed: "Open component {name}";
10229
+ readonly openPageNamed: "Open page {name}";
10070
10230
  };
10071
10231
  readonly zoom: {
10072
10232
  readonly label: "Zoom";
@@ -10272,8 +10432,8 @@ declare const en: {
10272
10432
  readonly disableItalic: "Disable italic";
10273
10433
  };
10274
10434
  readonly localization: {
10275
- readonly title: "Localization";
10276
- readonly textKey: "Text key";
10435
+ readonly translationKey: "Translation key";
10436
+ readonly noSourceLanguage: "The project has no source language";
10277
10437
  readonly none: "None";
10278
10438
  readonly createKey: "Create new key\u2026";
10279
10439
  readonly createKeyTitle: "New localization key";
@@ -11779,6 +11939,12 @@ declare const en: {
11779
11939
  readonly label: "About";
11780
11940
  readonly tooltip: "About NarraLeaf Studio";
11781
11941
  };
11942
+ readonly exportLogs: {
11943
+ readonly label: "Export Logs\u2026";
11944
+ readonly tooltip: "Save all Studio logs as a zip archive";
11945
+ readonly done: "Logs saved to {path}";
11946
+ readonly failed: "The logs could not be exported.";
11947
+ };
11782
11948
  };
11783
11949
  readonly export: {
11784
11950
  readonly chooseFolder: "Choose a folder for the exported project package.";
@@ -12116,7 +12282,13 @@ declare const en: {
12116
12282
  readonly title: "Layers";
12117
12283
  readonly empty: "Nothing is on screen";
12118
12284
  readonly stack: "Stack, bottom to top";
12285
+ readonly stage: "Stage";
12286
+ readonly gameUi: "Game UI";
12119
12287
  readonly page: "Page";
12288
+ readonly layer: "Layer";
12289
+ readonly faded: "Faded out";
12290
+ readonly hiddenForGame: "Hidden while the game runs";
12291
+ readonly returnsOnBack: "Shown again on Go back";
12120
12292
  readonly onScreenCount: "{onScreen} of {total} on screen";
12121
12293
  readonly offScreen: "Not on screen";
12122
12294
  readonly offScreenNote: "A layer stays off screen while this project has no surface with its id.";
@@ -12124,6 +12296,7 @@ declare const en: {
12124
12296
  readonly queued: "Waiting for a group";
12125
12297
  readonly takesClicks: "Takes clicks";
12126
12298
  readonly takesNoClicks: "Takes no clicks";
12299
+ readonly takesNoInput: "Takes no clicks or keys";
12127
12300
  readonly keyboard: "Keyboard";
12128
12301
  readonly modal: "Modal";
12129
12302
  readonly dismissible: "Go back closes it";
@@ -12495,6 +12668,7 @@ declare const en: {
12495
12668
  readonly inputAction: "input action";
12496
12669
  readonly asset: "asset";
12497
12670
  readonly page: "Page";
12671
+ readonly entryPage: "Entry Page";
12498
12672
  readonly gameUi: "Game UI";
12499
12673
  readonly image: "Image";
12500
12674
  readonly audio: "Audio";
@@ -12543,6 +12717,7 @@ declare const en: {
12543
12717
  readonly supported: "Supported files";
12544
12718
  readonly images: "Images";
12545
12719
  readonly log: "Log";
12720
+ readonly zip: "Zip archive";
12546
12721
  readonly text: "Text";
12547
12722
  readonly json: "JSON";
12548
12723
  readonly patch: "Patch";
@@ -13481,13 +13656,15 @@ declare const en: {
13481
13656
  };
13482
13657
  readonly localizationMissing: {
13483
13658
  readonly title: "Missing translation";
13484
- readonly description: "A line with no translation in a target language";
13659
+ readonly description: "A line or interface text with no translation in a target language";
13485
13660
  readonly message: "No {locale} translation";
13661
+ readonly messageInterface: "No {locale} translation of {text}";
13486
13662
  };
13487
13663
  readonly localizationStale: {
13488
13664
  readonly title: "Stale translation";
13489
13665
  readonly description: "The source line changed after it was translated";
13490
13666
  readonly message: "{locale} translation is older than the line";
13667
+ readonly messageInterface: "{locale} translation of {text} is older than the text";
13491
13668
  };
13492
13669
  readonly localizationMarkup: {
13493
13670
  readonly title: "Translation drops styling";
@@ -13793,6 +13970,7 @@ declare const en: {
13793
13970
  };
13794
13971
  readonly uiDocument: {
13795
13972
  readonly renamed: "Interface renamed";
13973
+ readonly entryPage: "Entry page changed";
13796
13974
  readonly surfaceAdded: "Surface added ({elementCount})";
13797
13975
  readonly surfaceRemoved: "Surface removed ({elementCount})";
13798
13976
  readonly surfaceChanged: "Surface changed";
@@ -13855,6 +14033,10 @@ declare const en: {
13855
14033
  readonly content: "File contents replaced";
13856
14034
  readonly field: "{field} changed";
13857
14035
  readonly orphanContent: "File with no asset record";
14036
+ readonly fields: {
14037
+ readonly group: "Group";
14038
+ readonly missingGroup: "Missing group";
14039
+ };
13858
14040
  };
13859
14041
  readonly brand: {
13860
14042
  readonly added: "Color added";
@@ -15047,7 +15229,7 @@ type Messages = typeof en;
15047
15229
  type Flatten<T, Prefix extends string = ""> = {
15048
15230
  [K in keyof T & string]: T[K] extends string ? `${Prefix}${K}` : Flatten<T[K], `${Prefix}${K}.`>;
15049
15231
  }[keyof T & string];
15050
- type TranslationKey = Flatten<Messages>;
15232
+ type TranslationKey = Flatten<Messages> | (string & {});
15051
15233
  type PrivilegedWriteBatchEntry = {
15052
15234
  path: string;
15053
15235
  encoding?: FsTextEncoding;
@@ -16357,6 +16539,11 @@ type LiveUIParts = {
16357
16539
  actions?: Readonly<Record<string, UIInputActionDef | null>>;
16358
16540
  /** The document's own name, when it changed. */
16359
16541
  name?: string;
16542
+ /**
16543
+ * Which page the game starts on, when that changed. `null` is the document naming none - which
16544
+ * is a value of its own (the main page is the entry), not "unchanged".
16545
+ */
16546
+ entrySurfaceId?: UISurfaceId | null;
16360
16547
  };
16361
16548
  type LiveUIElementRef = {
16362
16549
  componentId: UIComponentId | null;
@@ -16747,6 +16934,10 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
16747
16934
  private normalizeSpecialChildSlots;
16748
16935
  private createEmptyDocument;
16749
16936
  createSurface(input: CreateSurfaceInput): UISurface;
16937
+ /**
16938
+ * Remove a surface and its tree. The entry page is refused: a game has to start somewhere, and
16939
+ * the way to delete the page it starts on is to make another page the entry first.
16940
+ */
16750
16941
  deleteSurface(surfaceId: string): void;
16751
16942
  /**
16752
16943
  * Put the surfaces in the order given.
@@ -16769,6 +16960,21 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
16769
16960
  * the entry.
16770
16961
  */
16771
16962
  reorderSurfaces(orderedSurfaceIds: readonly string[], movedSurfaceId?: string): void;
16963
+ /**
16964
+ * Make this page the one the game starts on.
16965
+ *
16966
+ * Only a page can be the entry - a Game UI has nothing to show before a story runs - and asking
16967
+ * for the page that already is changes nothing and records nothing. The pages keep their ids:
16968
+ * the document stores which one is the entry (`UIDocument.entrySurfaceId`), so every blueprint
16969
+ * and every `Go Page` that names either page still names the same page afterwards.
16970
+ *
16971
+ * On the project stack, for the reason {@link reorderSurfaces} gives: it is an edit to no one
16972
+ * surface, made from the panel. The entry restores to what the file stored rather than to the
16973
+ * page that was resolved, so taking this back leaves a document that never named an entry as
16974
+ * short as it was.
16975
+ */
16976
+ setEntrySurface(surfaceId: string): void;
16977
+ private applyEntrySurface;
16772
16978
  private applySurfaceOrder;
16773
16979
  renameSurface(surfaceId: string, name: string): void;
16774
16980
  /**
@@ -16921,7 +17127,25 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
16921
17127
  private configureDefaultNotificationBlueprints;
16922
17128
  private configureDefaultChoiceBlueprints;
16923
17129
  private configureDefaultNvlBlueprints;
16924
- private ensureMainSurface;
17130
+ /**
17131
+ * A page for the game to start on, whatever the file said. True when the document had to change.
17132
+ *
17133
+ * Three repairs, each for a document that arrived broken - by a merge, a hand edit, or an older
17134
+ * tool - and none of which a document Studio wrote ever needs:
17135
+ *
17136
+ * - **no page at all**: one is made, under the main id when nothing holds it, so a project
17137
+ * always has somewhere to start;
17138
+ * - **an entry page whose root element is missing**: it gets an empty one, so it can be opened;
17139
+ * - **a stored entry that names no page**: the pointer is dropped, and the entry is the page the
17140
+ * document would name without it (`resolveEntrySurface` already reads it that way - this only
17141
+ * stops the file from saying something it does not mean).
17142
+ *
17143
+ * ⚠ **It never changes a page's id.** This used to give the first page the main id when no page
17144
+ * had it, which was harmless only while the main page could not be deleted. Now that the entry is
17145
+ * a pointer and the old main page can be deleted like any other, doing that would quietly re-file
17146
+ * every blueprint and every `Go Page` that named the page by the id it had.
17147
+ */
17148
+ private ensureEntrySurface;
16925
17149
  private getDocumentPath;
16926
17150
  private ensureDocumentDir;
16927
17151
  }
@@ -17186,6 +17410,8 @@ interface IconButtonGroupOption {
17186
17410
  icon: React$1.ReactNode;
17187
17411
  label?: string;
17188
17412
  disabled?: boolean;
17413
+ /** Hover text when it should say more than the label - why an option is unavailable, say. */
17414
+ tip?: string;
17189
17415
  }
17190
17416
  type IconButtonSelection = string | string[] | null;
17191
17417
  type IconButtonSegGroupDensity = "default" | "compact";
@@ -17672,6 +17898,20 @@ type BlueprintNodeDeclaration = {
17672
17898
  };
17673
17899
  inspectorParams?: BlueprintInspectorParamDef[];
17674
17900
  role?: BlueprintNodeRole;
17901
+ /**
17902
+ * The node's own words in other languages, for a node the host did not define.
17903
+ *
17904
+ * Keyed by locale code, then by the English text exactly as this declaration writes it: the
17905
+ * title, the category, a pin label, an inspector label, an option label or an empty-option
17906
+ * label. That is how the host's own catalogue is keyed (see `blueprintNodeI18n`), because the
17907
+ * canvas, the palette and the issue list all hold a title or a label rather than the node that
17908
+ * declared it. A string the host already translates keeps the host's wording, so one English
17909
+ * word reads the same on every node; a string with no entry for the active locale is drawn in
17910
+ * English.
17911
+ *
17912
+ * Built-in nodes leave this unset: their words live in Studio's catalogue.
17913
+ */
17914
+ translations?: Readonly<Record<string, Readonly<Record<string, string>>>>;
17675
17915
  };
17676
17916
  interface ProjectProps {
17677
17917
  projectPath: string;
@@ -19101,6 +19341,12 @@ export type RuntimePluginSaveMetadata = {
19101
19341
  * heavier capability.
19102
19342
  */
19103
19343
  export type RuntimePluginSaves = {
19344
+ /**
19345
+ * Every slot in the store, reserved ones included: the autosave ring, and any slot a plugin
19346
+ * keeps for itself (`contributes.reservedSaveIds`). This is direct store access, not the
19347
+ * player's view - `List Saves` is the one that leaves bookkeeping out - so a plugin can find its
19348
+ * own reserved slot here.
19349
+ */
19104
19350
  listIds(): Promise<string[]>;
19105
19351
  readMetadata(id: string): Promise<RuntimePluginSaveMetadata | null>;
19106
19352
  /** Present only with `saves.write`. Overwrites the slot. */
@@ -19618,10 +19864,15 @@ type DropEdge = "before" | "after";
19618
19864
  interface DropIndicatorProps {
19619
19865
  /** Which edge of the positioned parent the line sits on. */
19620
19866
  edge: DropEdge;
19867
+ /**
19868
+ * Which way the rows run. A list (the default) puts "before" on the top edge and draws the line
19869
+ * across; a grid that reads left to right puts it on the left edge and draws the line down.
19870
+ */
19871
+ axis?: "vertical" | "horizontal";
19621
19872
  /** Extra classes for the rare caller that has to nudge it; the line itself is not negotiable. */
19622
19873
  className?: string;
19623
19874
  }
19624
- declare function DropIndicator({ edge, className }: DropIndicatorProps): React$1.ReactElement;
19875
+ declare function DropIndicator({ edge, axis, className }: DropIndicatorProps): React$1.ReactElement;
19625
19876
  export interface AccordionProps {
19626
19877
  children: React$1.ReactNode;
19627
19878
  /** Default open item IDs */
package/dist/plugin.d.ts CHANGED
@@ -60,6 +60,8 @@ export {
60
60
  PluginLocalizationKeyEntry,
61
61
  PluginLocalizationService,
62
62
  PluginManifestEntries,
63
+ PluginManifestLocalized,
64
+ PluginManifestLocalizedText,
63
65
  PluginManifestV2,
64
66
  PluginMessageBundle,
65
67
  PluginPanelEmptyStateProps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "narraleaf-studio",
3
- "version": "0.9.0",
3
+ "version": "1.1.0",
4
4
  "description": "TypeScript declarations for the NarraLeaf Studio plugin API (narraleaf-studio/plugin and narraleaf-studio/runtime).",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",