narraleaf-studio 1.4.1 → 1.4.3
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/_api.d.ts +1591 -1415
- package/package.json +1 -1
package/dist/_api.d.ts
CHANGED
|
@@ -1736,1336 +1736,6 @@ type BlueprintAssetPinRef = {
|
|
|
1736
1736
|
*/
|
|
1737
1737
|
origin?: "stored" | "published";
|
|
1738
1738
|
};
|
|
1739
|
-
type AudioClipRegion = {
|
|
1740
|
-
/** Offset from the start of the clip, in milliseconds. Where playback starts. */
|
|
1741
|
-
inMs?: number;
|
|
1742
|
-
/** Offset from the start of the clip, in milliseconds. Where playback stops, or where a loop turns around. */
|
|
1743
|
-
outMs?: number;
|
|
1744
|
-
/**
|
|
1745
|
-
* Offset from the start of the clip, in milliseconds. Where each repeat returns to.
|
|
1746
|
-
*
|
|
1747
|
-
* Absent means "return to {@link inMs}" - a plain loop, and exactly what every record written
|
|
1748
|
-
* before this field existed means. Set past the in point it describes the standard VN
|
|
1749
|
-
* **intro→loop**: `inMs..loopStartMs` plays once, then `loopStartMs..outMs` repeats forever.
|
|
1750
|
-
*
|
|
1751
|
-
* Constrained to `inMs <= loopStartMs < outMs`. A value outside that window is dropped rather
|
|
1752
|
-
* than clamped: clamping would invent a loop point the author never marked, while dropping
|
|
1753
|
-
* degrades to the plain loop the two other markers already describe.
|
|
1754
|
-
*/
|
|
1755
|
-
loopStartMs?: number;
|
|
1756
|
-
/**
|
|
1757
|
-
* Playback gain in decibels, never above 0: the engine cannot raise a clip past its own level, so
|
|
1758
|
-
* balancing loudness means bringing the loud clips down. Absent is unity. Folded into a volume
|
|
1759
|
-
* only by {@link clipVolume}.
|
|
1760
|
-
*/
|
|
1761
|
-
gainDb?: number;
|
|
1762
|
-
};
|
|
1763
|
-
type StoredAudioClipRegion = Pick<AudioClipRegion, "inMs" | "outMs" | "loopStartMs">;
|
|
1764
|
-
type StoredAudioGain = {
|
|
1765
|
-
db: number;
|
|
1766
|
-
};
|
|
1767
|
-
type BlueprintOpenExternalRequest = {
|
|
1768
|
-
url: string;
|
|
1769
|
-
};
|
|
1770
|
-
type BlueprintOpenExternalOutcome = "opened" | "refused" | "failed";
|
|
1771
|
-
type BlueprintOpenExternalResult = {
|
|
1772
|
-
outcome: BlueprintOpenExternalOutcome;
|
|
1773
|
-
/** Human-readable reason, null when the page was handed over. */
|
|
1774
|
-
error: string | null;
|
|
1775
|
-
};
|
|
1776
|
-
type BlueprintGraphKind = "event" | "function" | "macro";
|
|
1777
|
-
type GameBuildPlatform = "windows" | "macos" | "linux" | "web" | "android" | "ios";
|
|
1778
|
-
declare const UI_STRUCT_FIELD_TYPES: readonly [
|
|
1779
|
-
"string",
|
|
1780
|
-
"number",
|
|
1781
|
-
"boolean",
|
|
1782
|
-
"image",
|
|
1783
|
-
"color",
|
|
1784
|
-
"json"
|
|
1785
|
-
];
|
|
1786
|
-
type UIStructFieldType = (typeof UI_STRUCT_FIELD_TYPES)[number];
|
|
1787
|
-
type UIStructField = {
|
|
1788
|
-
/**
|
|
1789
|
-
* Stable identity. Value bindings, node params and generated pins all address a field by this,
|
|
1790
|
-
* so renaming `key` costs nothing and reshuffling the list costs nothing.
|
|
1791
|
-
*/
|
|
1792
|
-
id: string;
|
|
1793
|
-
/** The property name this field occupies inside an item object. */
|
|
1794
|
-
key: string;
|
|
1795
|
-
/**
|
|
1796
|
-
* What the author called it, shown in the item table header and in every field picker.
|
|
1797
|
-
*
|
|
1798
|
-
* Optional because `key` is usually already the readable name; a label is what lets a field
|
|
1799
|
-
* whose key has to stay ASCII (it travels in JSON to a plugin, a save file, a story variable)
|
|
1800
|
-
* still read as words in the editor.
|
|
1801
|
-
*/
|
|
1802
|
-
label?: string;
|
|
1803
|
-
type: UIStructFieldType;
|
|
1804
|
-
};
|
|
1805
|
-
type UIStructId = string;
|
|
1806
|
-
type UIStructDef = {
|
|
1807
|
-
id: UIStructId;
|
|
1808
|
-
fields: UIStructField[];
|
|
1809
|
-
};
|
|
1810
|
-
type PluginPermissionPersistence = "temporary" | "permanent";
|
|
1811
|
-
type PluginFileSystemPermissionMode = "read" | "write" | "readwrite";
|
|
1812
|
-
/**
|
|
1813
|
-
* Install-time permissions split into two families, and the split is the whole
|
|
1814
|
-
* point:
|
|
1815
|
-
*
|
|
1816
|
-
* - **Author-declared** (`filesystem`, `api`) — privileged Studio controls the
|
|
1817
|
-
* plugin asks for explicitly in `permissions[]`.
|
|
1818
|
-
* - **Derived** (`runtime`, `sidecar`, `buildDependency`, `externalLink`,
|
|
1819
|
-
* `network`) — computed from `contributes` by {@link validatePluginManifest}
|
|
1820
|
-
* and *rejected* if written by
|
|
1821
|
-
* hand. A capability is declared in exactly one place, so what the prompt shows
|
|
1822
|
-
* and what the plugin can actually reach cannot drift apart. Adding a
|
|
1823
|
-
* capability widens the permission set, which makes the update re-prompt for
|
|
1824
|
-
* free (see `isPermissionSubset`).
|
|
1825
|
-
*/
|
|
1826
|
-
export type PluginInstallPermission = {
|
|
1827
|
-
kind: "filesystem";
|
|
1828
|
-
path: string;
|
|
1829
|
-
mode: PluginFileSystemPermissionMode;
|
|
1830
|
-
recursive: boolean;
|
|
1831
|
-
} | {
|
|
1832
|
-
kind: "api";
|
|
1833
|
-
capability: string;
|
|
1834
|
-
} | {
|
|
1835
|
-
/** Derived from `contributes.runtimeCapabilities`. */
|
|
1836
|
-
kind: "runtime";
|
|
1837
|
-
capability: PluginRuntimeCapability;
|
|
1838
|
-
} | {
|
|
1839
|
-
/** Derived from `contributes.sidecars`: a native child process shipped inside the author's game. */
|
|
1840
|
-
kind: "sidecar";
|
|
1841
|
-
id: string;
|
|
1842
|
-
/**
|
|
1843
|
-
* Which of the two sidecar shapes this is, carried through from the manifest because the
|
|
1844
|
-
* two are different promises: `executable` starts a separate binary, `node` runs the
|
|
1845
|
-
* plugin's own JavaScript under the game's Electron as Node. An author told only "this
|
|
1846
|
-
* ships a helper program" has not been told which one they are approving.
|
|
1847
|
-
*
|
|
1848
|
-
* Named apart from `kind` above on purpose - that one says which permission this is, this
|
|
1849
|
-
* one says what the sidecar runs.
|
|
1850
|
-
*/
|
|
1851
|
-
sidecarKind: PluginSidecarKind;
|
|
1852
|
-
/** `<platform>-<arch>` keys the sidecar ships binaries for. */
|
|
1853
|
-
platforms: string[];
|
|
1854
|
-
} | {
|
|
1855
|
-
/** Derived from `contributes.buildDependencies`: binaries downloaded at build time. */
|
|
1856
|
-
kind: "buildDependency";
|
|
1857
|
-
id: string;
|
|
1858
|
-
/** Distinct hostnames the binaries are fetched from. */
|
|
1859
|
-
hosts: string[];
|
|
1860
|
-
} | {
|
|
1861
|
-
/**
|
|
1862
|
-
* Derived from `contributes.externalLinks`: addresses outside the game this plugin may open
|
|
1863
|
-
* in the player's browser or platform handler.
|
|
1864
|
-
*
|
|
1865
|
-
* One permission carrying every pattern rather than one permission each, because what the
|
|
1866
|
-
* author is deciding is a single question - "may this plugin send the player out to these
|
|
1867
|
-
* places" - and a prompt that asked it four times would be four chances to stop reading.
|
|
1868
|
-
* `isPermissionSubset` still compares the patterns one by one, so adding one to a later
|
|
1869
|
-
* version widens the set and re-prompts.
|
|
1870
|
-
*
|
|
1871
|
-
* The patterns are the author's own strings, unrewritten: this list is what the prompt
|
|
1872
|
-
* shows, and normalizing a permission before showing it to the person approving it would
|
|
1873
|
-
* make the prompt and the manifest two slightly different documents.
|
|
1874
|
-
*/
|
|
1875
|
-
kind: "externalLink";
|
|
1876
|
-
patterns: string[];
|
|
1877
|
-
} | {
|
|
1878
|
-
/**
|
|
1879
|
-
* Derived from `contributes.network`: hosts this plugin's runtime code requests bytes from.
|
|
1880
|
-
*
|
|
1881
|
-
* A separate question from `externalLink` and never folded into it. Opening a page hands an
|
|
1882
|
-
* address to the browser the player already uses and nothing comes back; this fetches, and
|
|
1883
|
-
* what comes back runs inside the game. An author deciding about one has not decided about
|
|
1884
|
-
* the other.
|
|
1885
|
-
*
|
|
1886
|
-
* The patterns are also what a project's network allowlist shows attributed to this plugin,
|
|
1887
|
-
* which is the point of declaring them: a build narrowed to a list still reaches what the
|
|
1888
|
-
* author approved here, and the list can say so rather than silently making room.
|
|
1889
|
-
*/
|
|
1890
|
-
kind: "network";
|
|
1891
|
-
patterns: string[];
|
|
1892
|
-
};
|
|
1893
|
-
type PluginSidecarKind = "executable" | "node";
|
|
1894
|
-
declare const PluginRuntimeCapability: {
|
|
1895
|
-
/** `app.game.store` — plugin-scoped persistent key/value storage. */
|
|
1896
|
-
readonly Store: "store";
|
|
1897
|
-
/** `app.game.events` — game lifecycle and engine event subscription. */
|
|
1898
|
-
readonly Events: "events";
|
|
1899
|
-
/** `app.game.state.get` — read story variables. */
|
|
1900
|
-
readonly StateRead: "state.read";
|
|
1901
|
-
/** `app.game.state.set` — write story variables. */
|
|
1902
|
-
readonly StateWrite: "state.write";
|
|
1903
|
-
/** `app.game.saves` — list and read save metadata. */
|
|
1904
|
-
readonly SavesRead: "saves.read";
|
|
1905
|
-
/**
|
|
1906
|
-
* `app.game.saves.write` / `.load` — overwrite a save slot and load one.
|
|
1907
|
-
*
|
|
1908
|
-
* Separate from (and heavier than) `saves.read`: this can destroy a
|
|
1909
|
-
* playthrough. It exists because quick-save/quick-load is an ordinary thing
|
|
1910
|
-
* for a visual novel to ship — the built-in Quick Save plugin is exactly
|
|
1911
|
-
* this — and a capability model that cannot express a feature the product
|
|
1912
|
-
* already has would just push authors back around it.
|
|
1913
|
-
*/
|
|
1914
|
-
readonly SavesWrite: "saves.write";
|
|
1915
|
-
/** `app.game.ui.overlay` — draw on top of the game. */
|
|
1916
|
-
readonly UiOverlay: "ui.overlay";
|
|
1917
|
-
/** `app.game.assets` — resolve packaged asset URLs. */
|
|
1918
|
-
readonly Assets: "assets";
|
|
1919
|
-
/**
|
|
1920
|
-
* `app.game.locale` — read and observe the game language, and read the project's own
|
|
1921
|
-
* localized text through the same table and fallback chain the `Get Text` node uses.
|
|
1922
|
-
*/
|
|
1923
|
-
readonly Locale: "locale";
|
|
1924
|
-
/**
|
|
1925
|
-
* `app.game.menu` — own the menu bar above the game.
|
|
1926
|
-
*
|
|
1927
|
-
* Its own capability rather than part of anything else, because what it grants is *the chrome
|
|
1928
|
-
* the player reads the game through*: every row on that bar, and what each one does, is this
|
|
1929
|
-
* plugin's to decide. The rows still act through the game's own vocabulary (see
|
|
1930
|
-
* `@shared/types/gameMenu`) - nothing here reaches past what a blueprint could already do - but
|
|
1931
|
-
* an author approving this is handing over a surface their players will read as the game's own,
|
|
1932
|
-
* which is worth a name at install rather than arriving with something else.
|
|
1933
|
-
*
|
|
1934
|
-
* Absent on shells with no bar to give (the web export, Dev Mode), which is reported the way
|
|
1935
|
-
* every environment difference is: the member is not there.
|
|
1936
|
-
*/
|
|
1937
|
-
readonly Menu: "menu";
|
|
1938
|
-
/**
|
|
1939
|
-
* `app.game.story` — register a compile pass that observes each scene and injects engine
|
|
1940
|
-
* actions around its rows.
|
|
1941
|
-
*
|
|
1942
|
-
* The heaviest of the ten to read, and the one whose weight is easiest to miss: nothing here
|
|
1943
|
-
* touches the player's files, and the author still sees only their own document — but a pass
|
|
1944
|
-
* runs over *every* scene of the project, sees who speaks in each, and can put actions around
|
|
1945
|
-
* lines it did not write. That is a plugin editing the story, at the last moment before it
|
|
1946
|
-
* plays, and it is worth a name the author approves at install.
|
|
1947
|
-
*
|
|
1948
|
-
* It is not `state.write` in disguise and does not imply it: a pass builds actions, it does not
|
|
1949
|
-
* run them, and the one piece of runtime state it can touch is a scene-local flag the compiler
|
|
1950
|
-
* creates and owns (`SceneCompileContext.runtimeFlag`), never a story variable the author
|
|
1951
|
-
* declared.
|
|
1952
|
-
*/
|
|
1953
|
-
readonly StoryCompile: "story.compile";
|
|
1954
|
-
/**
|
|
1955
|
-
* `app.game.diagnostics` — read what the player's caches are holding.
|
|
1956
|
-
*
|
|
1957
|
-
* The lightest of the list to grant and the easiest to misread as heavier than it is. Nothing
|
|
1958
|
-
* here is authored data: it reports how many images the engine is holding, how many bytes those
|
|
1959
|
-
* cost, and what budgets they are being held against. It reads no story, no save and no
|
|
1960
|
-
* variable, and every member is a read - there is nothing to set.
|
|
1961
|
-
*
|
|
1962
|
-
* It is its own capability rather than free like `data` and `config` because a plugin that
|
|
1963
|
-
* watches memory is a plugin that runs continuously and reports numbers about the player's
|
|
1964
|
-
* machine, and an author should see that named at install. The performance inspector is the
|
|
1965
|
-
* plugin this exists for.
|
|
1966
|
-
*
|
|
1967
|
-
* Absent until a game session is live, which is the same rule every other engine-backed member
|
|
1968
|
-
* follows: a plugin's `setup()` runs during boot, and asking then answers null rather than
|
|
1969
|
-
* throwing.
|
|
1970
|
-
*/
|
|
1971
|
-
readonly Diagnostics: "diagnostics";
|
|
1972
|
-
/**
|
|
1973
|
-
* `app.game.process.memory()` — read how much memory the game's processes hold, as the
|
|
1974
|
-
* operating system counts it.
|
|
1975
|
-
*
|
|
1976
|
-
* Its own capability rather than a member of `diagnostics`, because what it reports is not the
|
|
1977
|
-
* game's: `diagnostics` describes the engine's caches, which are the game's own bookkeeping,
|
|
1978
|
-
* while this is the player's machine - how much of its memory every process of the game is
|
|
1979
|
-
* holding, the main process and the GPU process included. An author who approved the first did
|
|
1980
|
-
* not approve the second, and a plugin that already holds `diagnostics` must not gain this by
|
|
1981
|
-
* an update without being asked again - which is what a capability of its own guarantees.
|
|
1982
|
-
*
|
|
1983
|
-
* Read-only, and numbers only: sizes and what each process is for, never a path, an id or a
|
|
1984
|
-
* command line. Nothing is sent anywhere by the host; what a plugin does with a reading is
|
|
1985
|
-
* visible in the code the author installed.
|
|
1986
|
-
*
|
|
1987
|
-
* Absent where there are no processes to count - the web export - and narrowed in Dev Mode to
|
|
1988
|
-
* the window's own renderer, since everything around it is Studio's.
|
|
1989
|
-
*/
|
|
1990
|
-
readonly ProcessMemory: "process.memory";
|
|
1991
|
-
};
|
|
1992
|
-
type PluginRuntimeCapability = typeof PluginRuntimeCapability[keyof typeof PluginRuntimeCapability];
|
|
1993
|
-
/**
|
|
1994
|
-
* `app.game.sidecar` has no entry here on purpose: it exists exactly when
|
|
1995
|
-
* `contributes.sidecars` is non-empty. Declaring the sidecar *is* the request.
|
|
1996
|
-
*/
|
|
1997
|
-
export interface PluginIdentity {
|
|
1998
|
-
id: string;
|
|
1999
|
-
name?: string;
|
|
2000
|
-
version?: string;
|
|
2001
|
-
publisher?: string;
|
|
2002
|
-
}
|
|
2003
|
-
interface PluginPermissionRequestBase {
|
|
2004
|
-
requestId: string;
|
|
2005
|
-
plugin: PluginIdentity;
|
|
2006
|
-
reason?: string;
|
|
2007
|
-
requestedAt?: number;
|
|
2008
|
-
}
|
|
2009
|
-
export type PluginPermissionRequest = (PluginPermissionRequestBase & {
|
|
2010
|
-
kind: "trust";
|
|
2011
|
-
persistence?: PluginPermissionPersistence;
|
|
2012
|
-
}) | (PluginPermissionRequestBase & {
|
|
2013
|
-
kind: "filesystem";
|
|
2014
|
-
path: string;
|
|
2015
|
-
mode: PluginFileSystemPermissionMode;
|
|
2016
|
-
recursive: boolean;
|
|
2017
|
-
persistence: PluginPermissionPersistence;
|
|
2018
|
-
}) | (PluginPermissionRequestBase & {
|
|
2019
|
-
kind: "install";
|
|
2020
|
-
source: string;
|
|
2021
|
-
permissions?: PluginInstallPermission[];
|
|
2022
|
-
persistence?: PluginPermissionPersistence;
|
|
2023
|
-
}) | (PluginPermissionRequestBase & {
|
|
2024
|
-
kind: "api";
|
|
2025
|
-
capability: string;
|
|
2026
|
-
persistence?: PluginPermissionPersistence;
|
|
2027
|
-
});
|
|
2028
|
-
interface PluginPermissionGrantResult {
|
|
2029
|
-
requestId: string;
|
|
2030
|
-
pluginId: string;
|
|
2031
|
-
kind: PluginPermissionRequest["kind"];
|
|
2032
|
-
approved: boolean;
|
|
2033
|
-
persistence: PluginPermissionPersistence;
|
|
2034
|
-
grantedAt?: number;
|
|
2035
|
-
}
|
|
2036
|
-
export type PluginPermissionPromptResult = PluginPermissionGrantResult | null;
|
|
2037
|
-
declare const PluginManifestVersion = 2;
|
|
2038
|
-
/**
|
|
2039
|
-
* Per-target plugin entry files. Each entry is a prebundled ESM file relative
|
|
2040
|
-
* to the plugin package root. At least one target must be declared.
|
|
2041
|
-
*
|
|
2042
|
-
* - `studio`: loaded in the workspace window only; talks to the
|
|
2043
|
-
* `narraleaf-studio/plugin` host API (editor extensions).
|
|
2044
|
-
* - `runtime`: loaded in every game execution environment (Dev Mode window,
|
|
2045
|
-
* Preview, Production); talks to the `narraleaf-studio/runtime` host API
|
|
2046
|
-
* (game logic such as blueprint node execute bindings).
|
|
2047
|
-
*/
|
|
2048
|
-
export type PluginManifestEntries = {
|
|
2049
|
-
studio?: string;
|
|
2050
|
-
runtime?: string;
|
|
2051
|
-
};
|
|
2052
|
-
type PluginLocaleContribution = {
|
|
2053
|
-
code: string;
|
|
2054
|
-
nativeName?: string;
|
|
2055
|
-
englishName?: string;
|
|
2056
|
-
intl?: string;
|
|
2057
|
-
dir?: "ltr" | "rtl";
|
|
2058
|
-
messages: string;
|
|
2059
|
-
};
|
|
2060
|
-
type PluginBinaryPlatformKey = string;
|
|
2061
|
-
type PluginSidecarTargetContribution = {
|
|
2062
|
-
/** Executable (or, for `kind: "node"`, the .js file) to run. Must appear in `include`. */
|
|
2063
|
-
entry: string;
|
|
2064
|
-
/**
|
|
2065
|
-
* Everything shipped for this platform. Entries are package-relative paths,
|
|
2066
|
-
* or `dep:<buildDependencyId>/<path>` to pull an artifact produced by a
|
|
2067
|
-
* declared build dependency (that is how third-party redistributables that
|
|
2068
|
-
* we may not vendor ourselves reach the pack).
|
|
2069
|
-
*/
|
|
2070
|
-
include: string[];
|
|
2071
|
-
/**
|
|
2072
|
-
* `sha256` of every package-relative entry in `include`, hex. Verified at
|
|
2073
|
-
* install and again at pack time, so a tampered package fails to install
|
|
2074
|
-
* instead of silently shipping a different binary. `dep:` entries are covered
|
|
2075
|
-
* by the build dependency's own digest instead.
|
|
2076
|
-
*/
|
|
2077
|
-
sha256: Record<string, string>;
|
|
2078
|
-
};
|
|
2079
|
-
type PluginSidecarContribution = {
|
|
2080
|
-
/** Prefixed with the plugin id, like every other contributed identifier. */
|
|
2081
|
-
id: string;
|
|
2082
|
-
/**
|
|
2083
|
-
* `executable` spawns the binary directly and talks to it over stdin/stdout; `node` runs the
|
|
2084
|
-
* .js as an Electron utility process and talks to it over `process.parentPort`, which is the
|
|
2085
|
-
* one channel a utility process has - it has no stdin.
|
|
2086
|
-
*/
|
|
2087
|
-
kind: PluginSidecarKind;
|
|
2088
|
-
/**
|
|
2089
|
-
* Newline-delimited JSON, which is the framing and not the channel: where the frames travel is
|
|
2090
|
-
* {@link kind}'s business, and only an executable sidecar's travel over stdio. Either way
|
|
2091
|
-
* stderr stays a plain log channel.
|
|
2092
|
-
*
|
|
2093
|
-
* `stdio-jsonl` is the older spelling of the same thing, from when there was only one channel
|
|
2094
|
-
* it could mean. Manifests that say it keep working and always will - a published plugin is a
|
|
2095
|
-
* file somebody already shipped - but a new one should say `jsonl`.
|
|
2096
|
-
*/
|
|
2097
|
-
transport: "jsonl" | "stdio-jsonl";
|
|
2098
|
-
/** `onGameStart` spawns with the window; `onRequest` waits for the first call. */
|
|
2099
|
-
autostart: "onGameStart" | "onRequest";
|
|
2100
|
-
/** How long the handshake may take before the sidecar counts as unavailable. */
|
|
2101
|
-
startupTimeoutMs: number;
|
|
2102
|
-
/** Grace period between the shutdown message and SIGTERM. */
|
|
2103
|
-
shutdownTimeoutMs: number;
|
|
2104
|
-
restart: {
|
|
2105
|
-
maxRetries: number;
|
|
2106
|
-
backoffMs: number;
|
|
2107
|
-
};
|
|
2108
|
-
targets: Record<PluginBinaryPlatformKey, PluginSidecarTargetContribution>;
|
|
2109
|
-
};
|
|
2110
|
-
type PluginBuildDependencyTargetContribution = {
|
|
2111
|
-
url: string;
|
|
2112
|
-
/** Mandatory. Doubles as the cache key, so re-pointing the URL at identical bytes never re-downloads. */
|
|
2113
|
-
sha256: string;
|
|
2114
|
-
archive: "zip";
|
|
2115
|
-
/** Archive-internal path -> path inside the produced dependency directory. */
|
|
2116
|
-
files: Record<string, string>;
|
|
2117
|
-
} | {
|
|
2118
|
-
url: string;
|
|
2119
|
-
sha256: string;
|
|
2120
|
-
archive: "none";
|
|
2121
|
-
/** Name the downloaded file takes inside the produced dependency directory. */
|
|
2122
|
-
fileName: string;
|
|
2123
|
-
};
|
|
2124
|
-
type PluginBuildDependencyContribution = {
|
|
2125
|
-
/** Prefixed with the plugin id. Referenced from sidecar `include` as `dep:<id>/<path>`. */
|
|
2126
|
-
id: string;
|
|
2127
|
-
/** Shown to the author at install and in build logs; say what the binaries are. */
|
|
2128
|
-
description?: string;
|
|
2129
|
-
targets: Record<PluginBinaryPlatformKey, PluginBuildDependencyTargetContribution>;
|
|
2130
|
-
};
|
|
2131
|
-
declare const PLUGIN_BUILD_CONFIG_TYPES: readonly [
|
|
2132
|
-
"text",
|
|
2133
|
-
"secret"
|
|
2134
|
-
];
|
|
2135
|
-
type PluginBuildConfigType = (typeof PLUGIN_BUILD_CONFIG_TYPES)[number];
|
|
2136
|
-
declare const PLUGIN_BUILD_CONFIG_SCOPES: readonly [
|
|
2137
|
-
"global",
|
|
2138
|
-
"variant",
|
|
2139
|
-
"platform",
|
|
2140
|
-
"variant-platform"
|
|
2141
|
-
];
|
|
2142
|
-
type PluginBuildConfigScope = (typeof PLUGIN_BUILD_CONFIG_SCOPES)[number];
|
|
2143
|
-
type PluginBuildConfigFieldContribution = {
|
|
2144
|
-
/** Unique within the plugin. Keys the stored value; never displayed. */
|
|
2145
|
-
key: string;
|
|
2146
|
-
/** Author-facing name of the field. */
|
|
2147
|
-
label: string;
|
|
2148
|
-
/** One line saying what the value is for. */
|
|
2149
|
-
description?: string;
|
|
2150
|
-
/** `text` is stored in the project; `secret` is not - the project stores a handle. */
|
|
2151
|
-
type: PluginBuildConfigType;
|
|
2152
|
-
/** Which builds share one value. */
|
|
2153
|
-
scope: PluginBuildConfigScope;
|
|
2154
|
-
/** Platforms the field applies to. Absent means every platform. */
|
|
2155
|
-
platforms?: GameBuildPlatform[];
|
|
2156
|
-
/** A build that would ship without a value for this field is refused. */
|
|
2157
|
-
required?: boolean;
|
|
2158
|
-
};
|
|
2159
|
-
export type PluginContributes = {
|
|
2160
|
-
/** Blueprint node types this plugin provides (editor def + runtime execute). */
|
|
2161
|
-
blueprintNodes?: string[];
|
|
2162
|
-
/** Widget element types this plugin provides (editor module + runtime renderer). */
|
|
2163
|
-
widgets?: string[];
|
|
2164
|
-
/** Studio language packs: locales this plugin adds or fills. */
|
|
2165
|
-
locales?: PluginLocaleContribution[];
|
|
2166
|
-
/**
|
|
2167
|
-
* Plugin storage namespaces to publish with the game, readable at runtime
|
|
2168
|
-
* through `app.game.data.readJson(namespace)`.
|
|
2169
|
-
*
|
|
2170
|
-
* Plugin stores live under the project's `editor/` directory, which is never
|
|
2171
|
-
* packaged. A plugin whose runtime needs authored data (catalogs, tables)
|
|
2172
|
-
* must list those namespaces here. The list is an explicit allowlist so
|
|
2173
|
-
* editor-only plugin state cannot leak into a shipped game by accident.
|
|
2174
|
-
*/
|
|
2175
|
-
runtimeData?: string[];
|
|
2176
|
-
/**
|
|
2177
|
-
* Test ids this plugin registers with `app.services.tests` (see
|
|
2178
|
-
* `docs/plugin-test-protocol.md`). Declaring them lets the Launcher say what
|
|
2179
|
-
* a plugin checks before any of its code runs, and registering an
|
|
2180
|
-
* undeclared id throws at load.
|
|
2181
|
-
*
|
|
2182
|
-
* Unlike every other code-backed contribution here, this one derives no
|
|
2183
|
-
* install permission — a test only ever executes because the author picked
|
|
2184
|
-
* it out of the Run > Test dialog and pressed Start, so there is no ambient
|
|
2185
|
-
* capability to consent to at install time.
|
|
2186
|
-
*/
|
|
2187
|
-
tests?: string[];
|
|
2188
|
-
/**
|
|
2189
|
-
* Save ids the plugin keeps for its own bookkeeping - a quick-save slot, a checkpoint - rather
|
|
2190
|
-
* than slots a player chose.
|
|
2191
|
-
*
|
|
2192
|
-
* The game leaves them out of `List Saves`, and so out of every save screen built on it, the way
|
|
2193
|
-
* it leaves out its own autosave ring. Nothing else changes for them: the plugin still writes,
|
|
2194
|
-
* loads and finds them through `app.game.saves` (whose `listIds` is the raw store listing and
|
|
2195
|
-
* still includes them), and the explicit-id save nodes - `Load Save`, `Delete Save`, the
|
|
2196
|
-
* `Get Save` readers - take them like any other id.
|
|
2197
|
-
*
|
|
2198
|
-
* Prefixed with the plugin id like the type lists above, so a plugin can only reserve ids in its
|
|
2199
|
-
* own namespace and can never hide a slot the author's own `Save Game` wrote. That is also why it
|
|
2200
|
-
* derives no install permission: it reaches nothing the plugin did not already own.
|
|
2201
|
-
*/
|
|
2202
|
-
reservedSaveIds?: string[];
|
|
2203
|
-
/**
|
|
2204
|
-
* Capability domains the `runtime` entry may use. Each maps 1:1 onto a
|
|
2205
|
-
* namespace on `app.game`, and an undeclared domain is absent from that
|
|
2206
|
-
* object rather than present-and-throwing — so what the install prompt
|
|
2207
|
-
* listed and what the plugin can reach are the same set by construction.
|
|
2208
|
-
*/
|
|
2209
|
-
runtimeCapabilities?: PluginRuntimeCapability[];
|
|
2210
|
-
/** Native child processes shipped inside the author's game. */
|
|
2211
|
-
sidecars?: PluginSidecarContribution[];
|
|
2212
|
-
/** External binaries fetched (and cached) at build time. */
|
|
2213
|
-
buildDependencies?: PluginBuildDependencyContribution[];
|
|
2214
|
-
/**
|
|
2215
|
-
* Values the author supplies before a build can ship. Declaration only - see
|
|
2216
|
-
* {@link PluginBuildConfigFieldContribution} for why there is no registration API and why this
|
|
2217
|
-
* grants the plugin nothing.
|
|
2218
|
-
*/
|
|
2219
|
-
buildConfig?: PluginBuildConfigFieldContribution[];
|
|
2220
|
-
/**
|
|
2221
|
-
* Address patterns this plugin may hand to the player's browser or platform handler, through
|
|
2222
|
-
* `app.game.navigation.openExternal`.
|
|
2223
|
-
*
|
|
2224
|
-
* Unlike everything else here, this one is *not* a list of identifiers the plugin owns - it is a
|
|
2225
|
-
* list of places outside the game it may reach, so it derives an install permission the author
|
|
2226
|
-
* approves by name (see `PluginInstallPermission` `kind: "externalLink"`) and adding one to a
|
|
2227
|
-
* later version re-prompts.
|
|
2228
|
-
*
|
|
2229
|
-
* Patterns are matched structurally, never as strings: see `isExternalLinkPatternDeclared` in
|
|
2230
|
-
* `@shared/types/externalLinkPattern` for the exact semantics. Wildcards are allowed
|
|
2231
|
-
* (`https://*.example.com/app/*`) and the scheme is not restricted to `http(s)`, because the
|
|
2232
|
-
* storefronts a plugin integrates with speak their own (`steam://run/480`). A handful of schemes
|
|
2233
|
-
* are refused at validation whatever a manifest says; that list and its reasoning live in the
|
|
2234
|
-
* same module.
|
|
2235
|
-
*
|
|
2236
|
-
* This is separate from, and grants nothing towards, {@link network}: opening a page and
|
|
2237
|
-
* fetching bytes are different acts, and the Open Link node the author writes is a third thing
|
|
2238
|
-
* again, governed only by its scheme.
|
|
2239
|
-
*/
|
|
2240
|
-
externalLinks?: string[];
|
|
2241
|
-
/**
|
|
2242
|
-
* Address patterns this plugin's runtime code requests bytes from.
|
|
2243
|
-
*
|
|
2244
|
-
* Like {@link externalLinks} and unlike everything else here, this is a list of places outside
|
|
2245
|
-
* the game rather than identifiers the plugin owns, so it derives an install permission the
|
|
2246
|
-
* author approves by name (`PluginInstallPermission` `kind: "network"`) and adding one to a
|
|
2247
|
-
* later version re-prompts.
|
|
2248
|
-
*
|
|
2249
|
-
* Two rules narrower than the external-link patterns, both for the same reason - what comes
|
|
2250
|
-
* back from a fetch runs inside the game, while an opened page does not:
|
|
2251
|
-
*
|
|
2252
|
-
* - **`http(s)` only.** There is no storefront-scheme case here; a plugin that fetches speaks
|
|
2253
|
-
* HTTP.
|
|
2254
|
-
* - **The path is written out.** `https://api.example.com/*` is a declaration and
|
|
2255
|
-
* `https://api.example.com` is not, because the pattern language reads the second as the
|
|
2256
|
-
* single path `/`. It is refused rather than rewritten: this string is what the install
|
|
2257
|
-
* prompt shows, and a manifest whose text differs from what was approved is the one thing
|
|
2258
|
-
* this whole mechanism exists to prevent.
|
|
2259
|
-
*
|
|
2260
|
-
* What this reaches is *not* widened by the project's own network allowlist and does not widen
|
|
2261
|
-
* it. A build narrowed to a list still reaches these hosts - the author approved them at
|
|
2262
|
-
* install - and the list shows them attributed to this plugin rather than quietly making room.
|
|
2263
|
-
*/
|
|
2264
|
-
network?: string[];
|
|
2265
|
-
/**
|
|
2266
|
-
* The props of a contributed widget that hold words a player reads, by widget type.
|
|
2267
|
-
*
|
|
2268
|
-
* Each type named here must also be in {@link widgets}. A declared prop is treated as Studio's own
|
|
2269
|
-
* text and button labels are: the properties panel offers its words written directly or read
|
|
2270
|
-
* from a translation key, the canvas draws a key's words, the words go into the project's
|
|
2271
|
-
* translation table and exports, the project checks report a missing translation or key, and
|
|
2272
|
-
* the glyph check reads them. In the game the widget's renderer is handed the words already in
|
|
2273
|
-
* the player's language, in the prop it declared - it never resolves a key itself.
|
|
2274
|
-
*
|
|
2275
|
-
* Declared in the manifest rather than at registration because both entries need it - the
|
|
2276
|
-
* studio entry's widget is edited by it and the runtime entry's is drawn by it - and a
|
|
2277
|
-
* declaration only one of them made would be the half that drifts.
|
|
2278
|
-
*/
|
|
2279
|
-
widgetText?: Record<string, PluginWidgetTextContribution[]>;
|
|
2280
|
-
/**
|
|
2281
|
-
* The shapes of the rows this plugin's nodes hand out, so a list can be given one and a field
|
|
2282
|
-
* read from one by name.
|
|
2283
|
-
*
|
|
2284
|
-
* A node pin carrying them says `struct:<id>` or `array<struct:<id>>` - `Get Gallery` answers
|
|
2285
|
-
* `array<struct:narraleaf.gallery.entry>` - and from then on the plugin's rows are what the
|
|
2286
|
-
* engine's own are: a list picks the shape under Edit content and its fields are the plugin's
|
|
2287
|
-
* and locked, Get Field offers them, and a list handed rows that lack them is reported.
|
|
2288
|
-
*
|
|
2289
|
-
* Declared in the manifest rather than at registration because the editor, the running game and
|
|
2290
|
-
* the command-line checker all need the shape, and only the manifest is read by all three before
|
|
2291
|
-
* any plugin code runs. Ids must start with the plugin id.
|
|
2292
|
-
*/
|
|
2293
|
-
structs?: PluginStructContribution[];
|
|
2294
|
-
};
|
|
2295
|
-
type PluginStructContribution = {
|
|
2296
|
-
/** Prefixed with the plugin id: `narraleaf.gallery.entry`. */
|
|
2297
|
-
id: string;
|
|
2298
|
-
/** What the editor calls the shape - "Gallery entry". */
|
|
2299
|
-
name: string;
|
|
2300
|
-
/** `name` per editor locale, matched exactly as `localized` names are. */
|
|
2301
|
-
localized?: Record<string, string>;
|
|
2302
|
-
/** In the order the rows are read: the list's content editor shows them in this order. */
|
|
2303
|
-
fields: {
|
|
2304
|
-
key: string;
|
|
2305
|
-
type: UIStructFieldType;
|
|
2306
|
-
}[];
|
|
2307
|
-
};
|
|
2308
|
-
/**
|
|
2309
|
-
* One prop of a contributed widget whose value is words a player reads.
|
|
2310
|
-
*
|
|
2311
|
-
* `keyProp` is the prop the widget's translation key is kept in when the author reads the words from
|
|
2312
|
-
* one. It defaults to `<prop>LocalizationKey`, and it is the host's to write: the widget never reads it
|
|
2313
|
-
* to draw, because the words in `prop` already come from the key when there is one.
|
|
2314
|
-
*/
|
|
2315
|
-
export type PluginWidgetTextContribution = {
|
|
2316
|
-
/** The prop holding the words. */
|
|
2317
|
-
prop: string;
|
|
2318
|
-
/** The prop the translation key is kept in; `<prop>LocalizationKey` when not declared. */
|
|
2319
|
-
keyProp?: string;
|
|
2320
|
-
/** What the properties panel calls the words. The prop name when not declared. */
|
|
2321
|
-
label?: string;
|
|
2322
|
-
/** `label` per editor locale, matched exactly as `localized` names are. */
|
|
2323
|
-
localized?: Record<string, string>;
|
|
2324
|
-
/** Whether the words may run over several lines; one line when not declared. */
|
|
2325
|
-
multiline?: boolean;
|
|
2326
|
-
};
|
|
2327
|
-
/**
|
|
2328
|
-
* The plugin's name and description in one editor language. Either may be left out, and the plain
|
|
2329
|
-
* manifest field is shown in its place.
|
|
2330
|
-
*/
|
|
2331
|
-
export type PluginManifestLocalizedText = {
|
|
2332
|
-
name?: string;
|
|
2333
|
-
description?: string;
|
|
2334
|
-
};
|
|
2335
|
-
/**
|
|
2336
|
-
* `name` and `description` per editor locale, keyed by the exact code Studio's language setting
|
|
2337
|
-
* stores (`zh`, `ja`, or a code a language pack adds). There is no region matching: an entry under
|
|
2338
|
-
* `zh-CN` is never shown, because no Studio locale is called that.
|
|
2339
|
-
*/
|
|
2340
|
-
export type PluginManifestLocalized = Record<string, PluginManifestLocalizedText>;
|
|
2341
|
-
export type PluginManifestV2 = Omit<PluginIdentity, "id" | "name" | "version"> & Required<Pick<PluginIdentity, "id" | "name" | "version">> & {
|
|
2342
|
-
manifestVersion: typeof PluginManifestVersion;
|
|
2343
|
-
description?: string;
|
|
2344
|
-
/**
|
|
2345
|
-
* Translations of `name` and `description`, shown wherever Studio names the plugin to an author
|
|
2346
|
-
* whose editor is set to that language. The plain fields stay the plugin's identity: command
|
|
2347
|
-
* lines, project files and built games name it by them.
|
|
2348
|
-
*/
|
|
2349
|
-
localized?: PluginManifestLocalized;
|
|
2350
|
-
/**
|
|
2351
|
-
* Package-relative path to the thumbnail shown beside the plugin's name in
|
|
2352
|
-
* the Launcher list. Square, at most 512x512, and one of the extensions in
|
|
2353
|
-
* `PLUGIN_ICON_EXTENSIONS`. Plugins without one keep the name monogram.
|
|
2354
|
-
*/
|
|
2355
|
-
icon?: string;
|
|
2356
|
-
entries: PluginManifestEntries;
|
|
2357
|
-
contributes?: PluginContributes;
|
|
2358
|
-
permissions?: PluginInstallPermission[];
|
|
2359
|
-
};
|
|
2360
|
-
export type NormalizedPluginManifestV2 = PluginManifestV2 & {
|
|
2361
|
-
contributes: Required<PluginContributes>;
|
|
2362
|
-
permissions: PluginInstallPermission[];
|
|
2363
|
-
};
|
|
2364
|
-
type PluginInstallSource = {
|
|
2365
|
-
kind: "local-directory";
|
|
2366
|
-
path: string;
|
|
2367
|
-
} | {
|
|
2368
|
-
kind: "builtin";
|
|
2369
|
-
path: string;
|
|
2370
|
-
} | {
|
|
2371
|
-
kind: "registry";
|
|
2372
|
-
url: string;
|
|
2373
|
-
};
|
|
2374
|
-
export type PluginInstallRecord = {
|
|
2375
|
-
pluginId: string;
|
|
2376
|
-
installPath: string;
|
|
2377
|
-
enabled: boolean;
|
|
2378
|
-
builtIn: boolean;
|
|
2379
|
-
manifest: NormalizedPluginManifestV2;
|
|
2380
|
-
installSource: PluginInstallSource;
|
|
2381
|
-
installedAt: number;
|
|
2382
|
-
updatedAt: number;
|
|
2383
|
-
grantedManifestVersion?: string | null;
|
|
2384
|
-
/**
|
|
2385
|
-
* The permission set the user actually approved, recorded so a later version
|
|
2386
|
-
* that does not widen it can inherit the grant instead of re-prompting.
|
|
2387
|
-
* Absent on records written before this was tracked — the manifest at
|
|
2388
|
-
* `grantedManifestVersion` is the fallback source of truth for those.
|
|
2389
|
-
*/
|
|
2390
|
-
grantedPermissions?: PluginInstallPermission[] | null;
|
|
2391
|
-
lastError?: string | null;
|
|
2392
|
-
};
|
|
2393
|
-
type PluginStatus = "enabled" | "disabled" | "needsAuthorization" | "error";
|
|
2394
|
-
export type PluginListItem = {
|
|
2395
|
-
pluginId: string;
|
|
2396
|
-
manifest: NormalizedPluginManifestV2;
|
|
2397
|
-
/**
|
|
2398
|
-
* `app://` address of the declared icon, absent when the plugin ships none.
|
|
2399
|
-
* Resolved here rather than in the renderer because only the main process
|
|
2400
|
-
* knows where the package landed on disk.
|
|
2401
|
-
*/
|
|
2402
|
-
iconUrl?: string;
|
|
2403
|
-
installPath: string;
|
|
2404
|
-
enabled: boolean;
|
|
2405
|
-
builtIn: boolean;
|
|
2406
|
-
status: PluginStatus;
|
|
2407
|
-
installSource: PluginInstallSource;
|
|
2408
|
-
installedAt: number;
|
|
2409
|
-
updatedAt: number;
|
|
2410
|
-
grantedManifestVersion?: string | null;
|
|
2411
|
-
lastError?: string | null;
|
|
2412
|
-
};
|
|
2413
|
-
/** Descriptor handed to the workspace window: plugins with a studio entry. */
|
|
2414
|
-
export type WorkspacePluginDescriptor = {
|
|
2415
|
-
plugin: PluginIdentity;
|
|
2416
|
-
manifest: NormalizedPluginManifestV2;
|
|
2417
|
-
entryUrl: string;
|
|
2418
|
-
};
|
|
2419
|
-
/** Descriptor handed to game execution environments: plugins with a runtime entry. */
|
|
2420
|
-
export type RuntimePluginDescriptor = {
|
|
2421
|
-
plugin: PluginIdentity;
|
|
2422
|
-
manifest: NormalizedPluginManifestV2;
|
|
2423
|
-
entryUrl: string;
|
|
2424
|
-
/**
|
|
2425
|
-
* Published plugin storage, keyed by the namespaces declared in
|
|
2426
|
-
* `contributes.runtimeData`. Absent namespaces simply have no entry - a
|
|
2427
|
-
* plugin must tolerate missing data (the project may never have written it).
|
|
2428
|
-
*/
|
|
2429
|
-
data?: Record<string, unknown>;
|
|
2430
|
-
/**
|
|
2431
|
-
* This plugin's own `contributes.buildConfig` values, as the build that produced the pack
|
|
2432
|
-
* resolved them, keyed by field key. Only ever the declaring plugin's own values: the pack
|
|
2433
|
-
* holds one record per plugin and a descriptor is built from one entry.
|
|
2434
|
-
*
|
|
2435
|
-
* Absent outside a compiled pack - the editor and Dev Mode hand out descriptors that were never
|
|
2436
|
-
* built for a variant, so there is nothing resolved to carry.
|
|
2437
|
-
*/
|
|
2438
|
-
buildConfig?: Record<string, string>;
|
|
2439
|
-
};
|
|
2440
|
-
type GameMenuLabel = {
|
|
2441
|
-
readonly key?: string | null;
|
|
2442
|
-
readonly text: string;
|
|
2443
|
-
readonly words?: string | null;
|
|
2444
|
-
};
|
|
2445
|
-
declare const GAME_MENU_DYNAMIC_SOURCES: readonly [
|
|
2446
|
-
"textLanguage",
|
|
2447
|
-
"voiceLanguage",
|
|
2448
|
-
"windowScale"
|
|
2449
|
-
];
|
|
2450
|
-
type GameMenuDynamicSource = typeof GAME_MENU_DYNAMIC_SOURCES[number];
|
|
2451
|
-
type GameMenuAction =
|
|
2452
|
-
/** Open an app surface as a page. */
|
|
2453
|
-
{
|
|
2454
|
-
readonly type: "openPage";
|
|
2455
|
-
readonly surfaceId: string;
|
|
2456
|
-
}
|
|
2457
|
-
/** Open an app surface as a layer over whatever is on screen. */
|
|
2458
|
-
| {
|
|
2459
|
-
readonly type: "openLayer";
|
|
2460
|
-
readonly surfaceId: string;
|
|
2461
|
-
readonly modal?: boolean;
|
|
2462
|
-
readonly dismissible?: boolean;
|
|
2463
|
-
readonly group?: string | null;
|
|
2464
|
-
}
|
|
2465
|
-
/** End the playthrough and hand the screen to a page - the same act as the story's quit row. */
|
|
2466
|
-
| {
|
|
2467
|
-
readonly type: "quitToPage";
|
|
2468
|
-
readonly surfaceId: string;
|
|
2469
|
-
}
|
|
2470
|
-
/**
|
|
2471
|
-
* Close the game.
|
|
2472
|
-
*
|
|
2473
|
-
* The same act as the Quit Application node, which means the same terms: a graceful quit that
|
|
2474
|
-
* does NOT run the author's close handler (`On Window Close Requested` answers the window
|
|
2475
|
-
* frame's own close, and the node deliberately stands the guard aside so a quit the game asked
|
|
2476
|
-
* for is not asked back about). An author who wants their own "really quit?" screen binds a
|
|
2477
|
-
* function instead and calls the node at the end of it - which is the same answer this menu
|
|
2478
|
-
* gives for every other confirmation.
|
|
2479
|
-
*/
|
|
2480
|
-
| {
|
|
2481
|
-
readonly type: "quitApp";
|
|
2482
|
-
}
|
|
2483
|
-
/** Advance the line on screen. */
|
|
2484
|
-
| {
|
|
2485
|
-
readonly type: "next";
|
|
2486
|
-
}
|
|
2487
|
-
/** Auto-forward on/off. Ticked while on. */
|
|
2488
|
-
| {
|
|
2489
|
-
readonly type: "toggleAutoForward";
|
|
2490
|
-
}
|
|
2491
|
-
/** Skipping on/off. Ticked while running. */
|
|
2492
|
-
| {
|
|
2493
|
-
readonly type: "toggleSkipping";
|
|
2494
|
-
}
|
|
2495
|
-
/** Which lines skipping stops at. The two values are a radio pair, never one switch. */
|
|
2496
|
-
| {
|
|
2497
|
-
readonly type: "setSkipReadText";
|
|
2498
|
-
readonly value: boolean;
|
|
2499
|
-
}
|
|
2500
|
-
/** Show or hide the dialogue box. Ticked while shown. */
|
|
2501
|
-
| {
|
|
2502
|
-
readonly type: "toggleDialog";
|
|
2503
|
-
}
|
|
2504
|
-
/** Step back one line of history. Greyed out when there is none. */
|
|
2505
|
-
| {
|
|
2506
|
-
readonly type: "historyUndo";
|
|
2507
|
-
}
|
|
2508
|
-
/** Step forward again after an undo. Greyed out when there is none. */
|
|
2509
|
-
| {
|
|
2510
|
-
readonly type: "historyRedo";
|
|
2511
|
-
}
|
|
2512
|
-
/** Full-screen on/off. Ticked while full-screen, including when the OS did it. */
|
|
2513
|
-
| {
|
|
2514
|
-
readonly type: "toggleFullscreen";
|
|
2515
|
-
}
|
|
2516
|
-
/**
|
|
2517
|
-
* Call a function declared in the global blueprint.
|
|
2518
|
-
*
|
|
2519
|
-
* `fnRef` is the catalogue's own reference (see `fnCatalog`), and `args` are keyed by the head's
|
|
2520
|
-
* parameter pin ids. Everything the named actions above do not cover goes through here -
|
|
2521
|
-
* including every confirmation, because what a game asks before losing a playthrough is the
|
|
2522
|
-
* author's screen and not a native box in a language the game may not even be running in.
|
|
2523
|
-
*/
|
|
2524
|
-
| {
|
|
2525
|
-
readonly type: "fn";
|
|
2526
|
-
readonly fnRef: string;
|
|
2527
|
-
readonly args?: Readonly<Record<string, unknown>>;
|
|
2528
|
-
};
|
|
2529
|
-
type GameMenuItemSpec = {
|
|
2530
|
-
readonly kind: "separator";
|
|
2531
|
-
} | {
|
|
2532
|
-
readonly kind: "submenu";
|
|
2533
|
-
readonly label: GameMenuLabel;
|
|
2534
|
-
readonly items: readonly GameMenuItemSpec[];
|
|
2535
|
-
} | {
|
|
2536
|
-
readonly kind: "action";
|
|
2537
|
-
readonly label: GameMenuLabel;
|
|
2538
|
-
readonly action: GameMenuAction;
|
|
2539
|
-
} | {
|
|
2540
|
-
readonly kind: "dynamic";
|
|
2541
|
-
readonly source: GameMenuDynamicSource;
|
|
2542
|
-
};
|
|
2543
|
-
type GameMenuSpecMenu = {
|
|
2544
|
-
readonly label: GameMenuLabel;
|
|
2545
|
-
readonly items: readonly GameMenuItemSpec[];
|
|
2546
|
-
};
|
|
2547
|
-
type GameMenuSpec = {
|
|
2548
|
-
readonly menus: readonly GameMenuSpecMenu[];
|
|
2549
|
-
};
|
|
2550
|
-
/**
|
|
2551
|
-
* How much memory a running game costs the machine it runs on, process by process.
|
|
2552
|
-
*
|
|
2553
|
-
* Read in the main process from Electron's process metrics (`app.getAppMetrics()`), which is the
|
|
2554
|
-
* operating system's own account: the same working set a task manager shows. Nothing a page can
|
|
2555
|
-
* read gives this. `performance.memory` describes one JavaScript heap, and in Electron it is also
|
|
2556
|
-
* rounded to coarse buckets - the two arms of a measured comparison both read exactly 92.9 MB -
|
|
2557
|
-
* while the memory a visual novel actually spends is mostly outside any heap:
|
|
2558
|
-
* decoded pictures, textures in the GPU process, audio buffers, the main process serving and
|
|
2559
|
-
* decrypting assets. The whole game is several processes, and only the process that owns them can
|
|
2560
|
-
* see all of them.
|
|
2561
|
-
*
|
|
2562
|
-
* `process.memoryUsage()` in the main process was the other candidate and answers a narrower
|
|
2563
|
-
* question - the main process's own heap and buffers - which is the one process a game's pictures
|
|
2564
|
-
* are not in.
|
|
2565
|
-
*
|
|
2566
|
-
* Comments in English per project convention.
|
|
2567
|
-
*/
|
|
2568
|
-
/** What a process is for, in the words a reader of a memory report needs. */
|
|
2569
|
-
type GameProcessKind = "main" | "renderer" | "gpu" | "utility" | "other";
|
|
2570
|
-
type GameProcessMemoryEntry = {
|
|
2571
|
-
kind: GameProcessKind;
|
|
2572
|
-
/**
|
|
2573
|
-
* What the platform calls it, when it says: a utility process names its service ("Network
|
|
2574
|
-
* Service", "Audio Service", a sidecar's own name). Absent for the rest.
|
|
2575
|
-
*/
|
|
2576
|
-
name?: string;
|
|
2577
|
-
/** Whether this is the process the asking page runs in. */
|
|
2578
|
-
current: boolean;
|
|
2579
|
-
/** Memory currently resident in physical RAM, in bytes. */
|
|
2580
|
-
workingSetBytes: number;
|
|
2581
|
-
/** The most this process has ever had resident, in bytes. */
|
|
2582
|
-
peakWorkingSetBytes: number;
|
|
2583
|
-
/** Memory no other process shares, in bytes. Windows reports it; other platforms do not. */
|
|
2584
|
-
privateBytes?: number;
|
|
2585
|
-
};
|
|
2586
|
-
type GameProcessMemoryReading = {
|
|
2587
|
-
/**
|
|
2588
|
-
* What the processes below add up to.
|
|
2589
|
-
*
|
|
2590
|
-
* - `game` - every process the game is made of. A packaged game and a preview.
|
|
2591
|
-
* - `window` - only the process that draws this window. Dev Mode: the window is Studio's, and
|
|
2592
|
-
* the processes around it (Studio's main process, its GPU process, its other windows) are
|
|
2593
|
-
* shared with everything else Studio has open, so counting them would report Studio.
|
|
2594
|
-
*/
|
|
2595
|
-
scope: "game" | "window";
|
|
2596
|
-
processes: GameProcessMemoryEntry[];
|
|
2597
|
-
/** The sum of every process's working set, in bytes. */
|
|
2598
|
-
workingSetBytes: number;
|
|
2599
|
-
/** The sum of every process's private bytes, or null where the platform does not report them. */
|
|
2600
|
-
privateBytes: number | null;
|
|
2601
|
-
};
|
|
2602
|
-
declare const UI_INPUT_POINTER_GESTURES: readonly [
|
|
2603
|
-
"click",
|
|
2604
|
-
"doubleClick",
|
|
2605
|
-
"rightClick",
|
|
2606
|
-
"wheelUp",
|
|
2607
|
-
"wheelDown",
|
|
2608
|
-
"wheelLeft",
|
|
2609
|
-
"wheelRight",
|
|
2610
|
-
"longPress",
|
|
2611
|
-
"middleClick"
|
|
2612
|
-
];
|
|
2613
|
-
type UIInputPointerGesture = (typeof UI_INPUT_POINTER_GESTURES)[number];
|
|
2614
|
-
type UIInputBinding = {
|
|
2615
|
-
kind: "pointer";
|
|
2616
|
-
gesture: UIInputPointerGesture;
|
|
2617
|
-
} | {
|
|
2618
|
-
kind: "key";
|
|
2619
|
-
key: string;
|
|
2620
|
-
} | {
|
|
2621
|
-
kind: "gamepad";
|
|
2622
|
-
button: string;
|
|
2623
|
-
};
|
|
2624
|
-
type UIInputActionDef = {
|
|
2625
|
-
id: string;
|
|
2626
|
-
/** Author-facing name. */
|
|
2627
|
-
name: string;
|
|
2628
|
-
/** The bindings a surface gets unless it overrides them. */
|
|
2629
|
-
bindings: UIInputBinding[];
|
|
2630
|
-
};
|
|
2631
|
-
type UISurfaceActionEnablement = {
|
|
2632
|
-
actionId: string;
|
|
2633
|
-
/** Whether firing this action stops the input here. Default true. */
|
|
2634
|
-
consume?: boolean;
|
|
2635
|
-
};
|
|
2636
|
-
declare const UI_PAGE_ANIMATION_PRESETS: readonly [
|
|
2637
|
-
"none",
|
|
2638
|
-
"fade",
|
|
2639
|
-
"slide",
|
|
2640
|
-
"push",
|
|
2641
|
-
"zoom",
|
|
2642
|
-
"pop",
|
|
2643
|
-
"blur"
|
|
2644
|
-
];
|
|
2645
|
-
declare const UI_PAGE_ANIMATION_DIRECTIONS: readonly [
|
|
2646
|
-
"auto",
|
|
2647
|
-
"left",
|
|
2648
|
-
"right",
|
|
2649
|
-
"up",
|
|
2650
|
-
"down",
|
|
2651
|
-
"angle"
|
|
2652
|
-
];
|
|
2653
|
-
type UIPageAnimationPreset = (typeof UI_PAGE_ANIMATION_PRESETS)[number];
|
|
2654
|
-
type UIPageAnimationDirection = (typeof UI_PAGE_ANIMATION_DIRECTIONS)[number];
|
|
2655
|
-
type UIPageAnimationSettings = {
|
|
2656
|
-
enter: UIPageAnimationPreset;
|
|
2657
|
-
exit: UIPageAnimationPreset;
|
|
2658
|
-
enterDirection: UIPageAnimationDirection;
|
|
2659
|
-
exitDirection: UIPageAnimationDirection;
|
|
2660
|
-
enterAngleDegrees: number;
|
|
2661
|
-
exitAngleDegrees: number;
|
|
2662
|
-
enterDurationSeconds: number;
|
|
2663
|
-
exitDurationSeconds: number;
|
|
2664
|
-
/** Held before this host's own enter starts, counted from the moment its subtree begins entering. */
|
|
2665
|
-
enterDelaySeconds: number;
|
|
2666
|
-
/** Held before this host's own exit starts, counted from the moment its subtree begins exiting. */
|
|
2667
|
-
exitDelaySeconds: number;
|
|
2668
|
-
/** Added to each successive direct child, so a row of children arrives (and leaves) one after another. */
|
|
2669
|
-
childStaggerSeconds: number;
|
|
2670
|
-
/** Hold this host's own exit until every child has finished leaving. Chains through nested parents. */
|
|
2671
|
-
exitWaitsForChildren: boolean;
|
|
2672
|
-
/** Surfaces only: hold the incoming Page until this exit has finished. */
|
|
2673
|
-
exitBlocking: boolean;
|
|
2674
|
-
};
|
|
2675
|
-
type UISurfaceBackgroundFillMode = "cover" | "contain" | "stretch" | "tile";
|
|
2676
|
-
type UISurfaceBackgroundImage = {
|
|
2677
|
-
/** Image library asset id. Absent background = absent field, so this is never empty. */
|
|
2678
|
-
assetId: string;
|
|
2679
|
-
fillMode: UISurfaceBackgroundFillMode;
|
|
2680
|
-
};
|
|
2681
|
-
type UIDocumentVersion = number;
|
|
2682
|
-
type UIDocumentId = string;
|
|
2683
|
-
type UISurfaceId = string;
|
|
2684
|
-
type UIElementId = string;
|
|
2685
|
-
type UIDocument = {
|
|
2686
|
-
schemaVersion: UIDocumentVersion;
|
|
2687
|
-
id: UIDocumentId;
|
|
2688
|
-
name: string;
|
|
2689
|
-
surfaces: UISurface[];
|
|
2690
|
-
components?: UIComponentDefinition[];
|
|
2691
|
-
elements: Record<UIElementId, UIElement>;
|
|
2692
|
-
/**
|
|
2693
|
-
* The shapes this document's widgets describe their data with, keyed by id.
|
|
2694
|
-
*
|
|
2695
|
-
* No authoring surface manages this table: an author edits the fields on the widget that uses
|
|
2696
|
-
* them, and a widget stores only the id. It is a document-level table rather than a copy on each
|
|
2697
|
-
* widget so that a shape has one spelling for the whole project - which is what lets one list's
|
|
2698
|
-
* content feed another's, and what a Break node's pins are generated from. The rules that keep
|
|
2699
|
-
* an implicit library safe to edit are in `struct.ts`.
|
|
2700
|
-
*/
|
|
2701
|
-
structs?: Record<UIStructId, UIStructDef>;
|
|
2702
|
-
/**
|
|
2703
|
-
* What the gestures of this project mean, keyed by id.
|
|
2704
|
-
*
|
|
2705
|
-
* The vocabulary half of the input model: "advance", "skip", "open the menu" - named once for
|
|
2706
|
-
* the whole project, with the bindings a surface gets unless it says otherwise. A surface stores
|
|
2707
|
-
* only the id, in {@link UISurface.actions}, so renaming an action here renames it everywhere
|
|
2708
|
-
* and rebinding it rebinds every surface that took the default. The rules are in
|
|
2709
|
-
* `inputAction.ts`.
|
|
2710
|
-
*/
|
|
2711
|
-
actions?: Record<string, UIInputActionDef>;
|
|
2712
|
-
/**
|
|
2713
|
-
* The page the game starts on, by id.
|
|
2714
|
-
*
|
|
2715
|
-
* Absent means the page carrying `MAIN_APP_SURFACE_ID` - the id a new document gives its first
|
|
2716
|
-
* page, and the only entry a document had before this field existed - so a document whose author
|
|
2717
|
-
* never moved the entry stays exactly as short as it was. Read only through `resolveEntrySurface`
|
|
2718
|
-
* (`entrySurface.ts`), which also says what a pointer that has lost its page falls back to.
|
|
2719
|
-
*
|
|
2720
|
-
* A pointer rather than an id swap: a page's id is what its blueprints are filed under and what
|
|
2721
|
-
* every `Go Page` names, so making another page the entry must not change which id it has.
|
|
2722
|
-
*
|
|
2723
|
-
* Carries no schema bump, for the reason `actions` and `structs` did not: absence already means a
|
|
2724
|
-
* defined answer, so a document written before this field reads correctly without one.
|
|
2725
|
-
*/
|
|
2726
|
-
entrySurfaceId?: UISurfaceId;
|
|
2727
|
-
meta?: UIDocumentMeta;
|
|
2728
|
-
};
|
|
2729
|
-
type UIDocumentMeta = {
|
|
2730
|
-
createdAt?: string;
|
|
2731
|
-
updatedAt?: string;
|
|
2732
|
-
};
|
|
2733
|
-
type UIHost = "app" | "player";
|
|
2734
|
-
type UISurfaceKind = "appSurface" | "stageSurface";
|
|
2735
|
-
type UIStageSlotId = "onStage" | "dialog" | "notification" | "choice" | "nvl";
|
|
2736
|
-
type UIStageSurfaceMount = {
|
|
2737
|
-
kind: "slot";
|
|
2738
|
-
slotId: UIStageSlotId;
|
|
2739
|
-
};
|
|
2740
|
-
type UIAppSurface = {
|
|
2741
|
-
id: UISurfaceId;
|
|
2742
|
-
name: string;
|
|
2743
|
-
host: "app";
|
|
2744
|
-
kind: "appSurface";
|
|
2745
|
-
designSize: UISurfaceDesignSize;
|
|
2746
|
-
rootElementId: UIElementId;
|
|
2747
|
-
settings?: UISurfaceSettings;
|
|
2748
|
-
/**
|
|
2749
|
-
* Which of the project's actions this surface answers. An id here names an entry of
|
|
2750
|
-
* {@link UIDocument.actions}; absent means it answers none of them.
|
|
2751
|
-
*
|
|
2752
|
-
* Input that lands on this surface's content stops here whether or not an action answered it,
|
|
2753
|
-
* unless the action that answered says otherwise. There is no surface-wide setting for that: a
|
|
2754
|
-
* surface drawn over another is drawn over it, and an action is the only thing that knows
|
|
2755
|
-
* whether what it just did leaves anything for the surface behind to do.
|
|
2756
|
-
*/
|
|
2757
|
-
actions?: UISurfaceActionEnablement[];
|
|
2758
|
-
/** The values this page is opened with, in author order. See {@link UIPageParam}. */
|
|
2759
|
-
params?: UIPageParam[];
|
|
2760
|
-
};
|
|
2761
|
-
type UIPageParam = {
|
|
2762
|
-
id: string;
|
|
2763
|
-
name: string;
|
|
2764
|
-
type: UIPageParamType;
|
|
2765
|
-
/**
|
|
2766
|
-
* For a `list`: the shape each row has, as a struct id (an engine shape such as `nl.confirmButton`,
|
|
2767
|
-
* a plugin's, or one of the project's). It types the value on both ends - the inputs that give it
|
|
2768
|
-
* and `Get Page Param` - and a list on the page bound to it is checked against it. Absent for
|
|
2769
|
-
* every other type, and for a list of rows of no declared shape.
|
|
2770
|
-
*/
|
|
2771
|
-
struct?: string;
|
|
2772
|
-
/**
|
|
2773
|
-
* What the page reads when whoever opened it gave nothing for this name, in the declared type.
|
|
2774
|
-
* Absent reads as the type's empty value (`uiPageParamDefaultValue`).
|
|
2775
|
-
*/
|
|
2776
|
-
defaultValue?: unknown;
|
|
2777
|
-
};
|
|
2778
|
-
type UIPageParamType = "string" | "text" | "number" | "boolean" | "list" | "json";
|
|
2779
|
-
type UIStageSurface = {
|
|
2780
|
-
id: UISurfaceId;
|
|
2781
|
-
name: string;
|
|
2782
|
-
host: "player";
|
|
2783
|
-
kind: "stageSurface";
|
|
2784
|
-
designSize: UISurfaceDesignSize;
|
|
2785
|
-
rootElementId: UIElementId;
|
|
2786
|
-
settings?: UISurfaceSettings;
|
|
2787
|
-
mount: UIStageSurfaceMount;
|
|
2788
|
-
slots?: Record<string, UISlotDefinition>;
|
|
2789
|
-
/** @see UIAppSurface.actions */
|
|
2790
|
-
actions?: UISurfaceActionEnablement[];
|
|
2791
|
-
};
|
|
2792
|
-
type UISurface = UIAppSurface | UIStageSurface;
|
|
2793
|
-
type UISurfaceDesignSize = {
|
|
2794
|
-
width: number;
|
|
2795
|
-
height: number;
|
|
2796
|
-
};
|
|
2797
|
-
type UISurfaceSettings = {
|
|
2798
|
-
backgroundColor?: string;
|
|
2799
|
-
/**
|
|
2800
|
-
* Painted over {@link backgroundColor}, inside the design area only - the letterbox bars around
|
|
2801
|
-
* a scaled Surface keep showing the colour, so the two settings are not alternatives.
|
|
2802
|
-
*/
|
|
2803
|
-
backgroundImage?: UISurfaceBackgroundImage;
|
|
2804
|
-
pageAnimation?: UIPageAnimationSettings;
|
|
2805
|
-
/**
|
|
2806
|
-
* What the asset set this Surface's own background names resolves to, per locale. Build-written
|
|
2807
|
-
* exactly as {@link UIElement.assetVariants} is, and here rather than on the Surface so the
|
|
2808
|
-
* answer sits beside the only asset a Surface names by itself.
|
|
2809
|
-
*/
|
|
2810
|
-
assetVariants?: AssetVariantMap;
|
|
2811
|
-
};
|
|
2812
|
-
type UISlotDefinition = {
|
|
2813
|
-
id: string;
|
|
2814
|
-
name: string;
|
|
2815
|
-
rootElementId?: UIElementId;
|
|
2816
|
-
};
|
|
2817
|
-
type UIComponentId = string;
|
|
2818
|
-
type UIComponentDefinition = {
|
|
2819
|
-
id: UIComponentId;
|
|
2820
|
-
name: string;
|
|
2821
|
-
rootElementId: UIElementId;
|
|
2822
|
-
elements: Record<UIElementId, UIElement>;
|
|
2823
|
-
/** Declared in the component's own inspector; each instance supplies its own values. */
|
|
2824
|
-
params?: UIComponentParam[];
|
|
2825
|
-
previewMeta?: {
|
|
2826
|
-
width?: number;
|
|
2827
|
-
height?: number;
|
|
2828
|
-
};
|
|
2829
|
-
createdAt?: string;
|
|
2830
|
-
updatedAt?: string;
|
|
2831
|
-
};
|
|
2832
|
-
type UIComponentParam = {
|
|
2833
|
-
/** Stable identity. Blueprints and instance values reference this, so renaming `name` is free. */
|
|
2834
|
-
id: string;
|
|
2835
|
-
/** What the author called it, shown in the instance's inspector. */
|
|
2836
|
-
name: string;
|
|
2837
|
-
type: UIComponentParamType;
|
|
2838
|
-
/**
|
|
2839
|
-
* Used by an instance that has not overridden it, and by the definition's own preview. For a
|
|
2840
|
-
* `text` parameter these are words written directly, translated through the definition's own unit
|
|
2841
|
-
* (`uiComponentParamUnitId(componentId, paramId)`).
|
|
2842
|
-
*/
|
|
2843
|
-
defaultValue: string;
|
|
2844
|
-
};
|
|
2845
|
-
type UIComponentParamType = "string" | "text";
|
|
2846
|
-
type UIElement = {
|
|
2847
|
-
id: UIElementId;
|
|
2848
|
-
type: string;
|
|
2849
|
-
name?: string;
|
|
2850
|
-
parentId: UIElementId | null;
|
|
2851
|
-
childrenIds: UIElementId[];
|
|
2852
|
-
layout: UILayout;
|
|
2853
|
-
style?: UIStyle;
|
|
2854
|
-
props?: Record<string, unknown>;
|
|
2855
|
-
valueBindings?: Record<string, UIElementValueBinding>;
|
|
2856
|
-
/**
|
|
2857
|
-
* How this element arrives and leaves - the same record a Surface uses for its page animation.
|
|
2858
|
-
* Absent means it does neither. Read it through `elementAnimation.ts` rather than directly.
|
|
2859
|
-
*
|
|
2860
|
-
* Its own field rather than a `props` key, because it belongs to the placement and not to the
|
|
2861
|
-
* widget: every type has one, no renderer reads it, and a linked component instance may set it
|
|
2862
|
-
* even though its props come from the definition.
|
|
2863
|
-
*/
|
|
2864
|
-
animation?: UIPageAnimationSettings;
|
|
2865
|
-
extra?: Record<string, unknown>;
|
|
2866
|
-
/**
|
|
2867
|
-
* What each asset set THIS element names resolves to, per locale.
|
|
2868
|
-
*
|
|
2869
|
-
* **Never authored and never on disk under `editor/`.** It is written while a package is being
|
|
2870
|
-
* assembled (`@shared/build/uiAssetSets`) and exists only in the bundle a game runs from, which
|
|
2871
|
-
* is why editing a widget cannot produce one and why version control never sees it.
|
|
2872
|
-
*
|
|
2873
|
-
* It sits on the element rather than in a document-wide table for the reason the story row's
|
|
2874
|
-
* copy gives: a table would enumerate every localized asset the project has, which is the one
|
|
2875
|
-
* thing a protected pack refuses to disclose. One element discloses the variants of the sets
|
|
2876
|
-
* that element itself uses, which is what drawing the widget already discloses.
|
|
2877
|
-
*
|
|
2878
|
-
* Keyed by the set id the props still name - the prop keeps the set id, so a widget whose map is
|
|
2879
|
-
* missing fails loudly at a resolver rather than quietly fetching one language's picture.
|
|
2880
|
-
*/
|
|
2881
|
-
assetVariants?: AssetVariantMap;
|
|
2882
|
-
};
|
|
2883
|
-
type UIElementValueBindingValueType = "string" | "json" | "float" | "boolean";
|
|
2884
|
-
type UIElementValueBinding = {
|
|
2885
|
-
kind: "blueprintValue";
|
|
2886
|
-
blueprintId: string;
|
|
2887
|
-
valueType: UIElementValueBindingValueType;
|
|
2888
|
-
}
|
|
2889
|
-
/**
|
|
2890
|
-
* Read one declared field of the list item this element is being drawn for.
|
|
2891
|
-
*
|
|
2892
|
-
* The whole reason the struct model exists. Reaching a row's own data used to take a private
|
|
2893
|
-
* blueprint per element - Get List Item Props into Get JSON Field with the key typed into a
|
|
2894
|
-
* string pin - which is three abstractions deep for "show the title". This is the same read with
|
|
2895
|
-
* none of them: the field is picked from a dropdown, and an element outside an item template
|
|
2896
|
-
* simply has nothing to bind to.
|
|
2897
|
-
*
|
|
2898
|
-
* No `valueType` of its own. The field declares one and the bound prop declares one, and the
|
|
2899
|
-
* conversion between them belongs to the target, not to the binding - the same reason the
|
|
2900
|
-
* slider's binding does not restate that a slider holds a number.
|
|
2901
|
-
*/
|
|
2902
|
-
| {
|
|
2903
|
-
kind: "listItemField";
|
|
2904
|
-
fieldId: string;
|
|
2905
|
-
}
|
|
2906
|
-
/**
|
|
2907
|
-
* Show the value one placement gives a text parameter of the component this element is part of.
|
|
2908
|
-
*
|
|
2909
|
-
* The component's counterpart of `listItemField`, and the same kind of read: no graph, nothing
|
|
2910
|
-
* kept alive - the value is taken from the placement being drawn (`resolveUIComponentTextParams`),
|
|
2911
|
-
* so the editing canvas draws each placement with its own words, and the game translates each
|
|
2912
|
-
* through the placement's own unit or reads the key it names. Only a text parameter answers one,
|
|
2913
|
-
* and only a widget's words take one; outside a placement (the component's own editor) the
|
|
2914
|
-
* element's own words are drawn, as sample text.
|
|
2915
|
-
*/
|
|
2916
|
-
| {
|
|
2917
|
-
kind: "componentParam";
|
|
2918
|
-
paramId: string;
|
|
2919
|
-
}
|
|
2920
|
-
/**
|
|
2921
|
-
* Show the value the page this element is on was opened with for one of its text parameters.
|
|
2922
|
-
*
|
|
2923
|
-
* The page's counterpart of `componentParam`, read the same way: no graph - the value is taken from
|
|
2924
|
-
* the props the page was opened with (`resolveUIPageTextParams`), words given at the opening shown
|
|
2925
|
-
* as given, the declared default translated through the page's own unit. Only a text parameter
|
|
2926
|
-
* answers one, only a widget's words take one, and only on a page: a component's insides and a
|
|
2927
|
-
* Game UI have no page parameters to show. The editing canvas, where the page has not been opened,
|
|
2928
|
-
* draws the default, or the element's own words as sample text while the default is empty.
|
|
2929
|
-
*/
|
|
2930
|
-
| {
|
|
2931
|
-
kind: "pageParam";
|
|
2932
|
-
paramId: string;
|
|
2933
|
-
};
|
|
2934
|
-
type UILayout = {
|
|
2935
|
-
x: number;
|
|
2936
|
-
y: number;
|
|
2937
|
-
width: number;
|
|
2938
|
-
height: number;
|
|
2939
|
-
rotation?: number;
|
|
2940
|
-
opacity?: number;
|
|
2941
|
-
visible?: boolean;
|
|
2942
|
-
/** When true, width and height stay proportional in the inspector and on-canvas resize. */
|
|
2943
|
-
lockAspectRatio?: boolean;
|
|
2944
|
-
};
|
|
2945
|
-
type UIStyle = Record<string, unknown>;
|
|
2946
|
-
type GameTestLogLevel = "verbose" | "info" | "success" | "warning" | "error";
|
|
2947
|
-
type GameTestExitReason =
|
|
2948
|
-
/** Closed from inside the game - the window's own close, or the engine quitting itself. */
|
|
2949
|
-
"closed-by-user"
|
|
2950
|
-
/** The host asked: an explicit stop, or the run being cancelled. */
|
|
2951
|
-
| "stopped-by-host"
|
|
2952
|
-
/** Non-zero exit code, a fatal signal, or an uncaught exception in the game's main process. */
|
|
2953
|
-
| "crashed"
|
|
2954
|
-
/** Never got far enough to run: compile failed, or the runner binary would not spawn. */
|
|
2955
|
-
| "failed-to-start";
|
|
2956
|
-
type GameTestExit = {
|
|
2957
|
-
reason: GameTestExitReason;
|
|
2958
|
-
code: number | null;
|
|
2959
|
-
signal: string | null;
|
|
2960
|
-
};
|
|
2961
|
-
type GameTestChoiceOption = {
|
|
2962
|
-
index: number;
|
|
2963
|
-
text: string;
|
|
2964
|
-
/** Offered but not selectable: the option's `disabledWhen` condition holds. */
|
|
2965
|
-
disabled: boolean;
|
|
2966
|
-
};
|
|
2967
|
-
type GameTestEvent = {
|
|
2968
|
-
kind: "console";
|
|
2969
|
-
level: GameTestLogLevel;
|
|
2970
|
-
source: string;
|
|
2971
|
-
message: string;
|
|
2972
|
-
}
|
|
2973
|
-
/**
|
|
2974
|
-
* An uncaught error inside the running game - `window.onerror` / `unhandledrejection` in its
|
|
2975
|
-
* renderer, `uncaughtException` / `unhandledRejection` in its main process. The game runtime had
|
|
2976
|
-
* no such hook before this pipeline; an uncaught renderer exception was reported nowhere.
|
|
2977
|
-
*/
|
|
2978
|
-
| {
|
|
2979
|
-
kind: "runtime-error";
|
|
2980
|
-
scope: "renderer" | "main";
|
|
2981
|
-
message: string;
|
|
2982
|
-
stack?: string;
|
|
2983
|
-
}
|
|
2984
|
-
/** The engine reached an ending (`event:state.end`), forwarded out of the game's renderer. */
|
|
2985
|
-
| {
|
|
2986
|
-
kind: "game-end";
|
|
2987
|
-
}
|
|
2988
|
-
/**
|
|
2989
|
-
* An `/ending` row ran, naming the ending it declares.
|
|
2990
|
-
*
|
|
2991
|
-
* Beside `game-end` rather than replacing it: `game-end` also fires for a story that simply ran
|
|
2992
|
-
* out of rows, which has no ending to name. A test whose pass condition is "*this* ending" can
|
|
2993
|
-
* only be written against this one.
|
|
2994
|
-
*/
|
|
2995
|
-
| {
|
|
2996
|
-
kind: "ending";
|
|
2997
|
-
endingId: string;
|
|
2998
|
-
name: string;
|
|
2999
|
-
}
|
|
3000
|
-
/**
|
|
3001
|
-
* A choice menu is on screen, with every option it evaluated.
|
|
3002
|
-
*
|
|
3003
|
-
* Fires each time a menu mounts, so a rollback into the same choice reports it again. The list
|
|
3004
|
-
* is what `choose` addresses; see {@link GameTestChoiceOption}.
|
|
3005
|
-
*/
|
|
3006
|
-
| {
|
|
3007
|
-
kind: "choice";
|
|
3008
|
-
options: GameTestChoiceOption[];
|
|
3009
|
-
} | {
|
|
3010
|
-
kind: "exit";
|
|
3011
|
-
exit: GameTestExit;
|
|
3012
|
-
};
|
|
3013
|
-
type GameTestCommand =
|
|
3014
|
-
/**
|
|
3015
|
-
* Begin a story at a scene.
|
|
3016
|
-
*
|
|
3017
|
-
* Necessary because a test session boots to the main app surface - the title screen - and no
|
|
3018
|
-
* story runs until something starts one. The caller supplies the target, exactly as the
|
|
3019
|
-
* blueprint behind a Start button does.
|
|
3020
|
-
*/
|
|
3021
|
-
{
|
|
3022
|
-
kind: "start";
|
|
3023
|
-
storyId: string;
|
|
3024
|
-
sceneId: string;
|
|
3025
|
-
}
|
|
3026
|
-
/** One click on the dialogue: finish the line being typed, or move to the next. */
|
|
3027
|
-
| {
|
|
3028
|
-
kind: "advance";
|
|
3029
|
-
}
|
|
3030
|
-
/** Pick an option of the choice currently on screen, by the index the `choice` event reported. */
|
|
3031
|
-
| {
|
|
3032
|
-
kind: "choose";
|
|
3033
|
-
index: number;
|
|
3034
|
-
};
|
|
3035
|
-
type UITemplateSurfacePlacement = {
|
|
3036
|
-
kind: UISurfaceKind;
|
|
3037
|
-
/** Required for `stageSurface`; the game-UI slot the surface mounts into. */
|
|
3038
|
-
slotId?: UIStageSlotId;
|
|
3039
|
-
};
|
|
3040
|
-
interface AssetTransferManifestEntry {
|
|
3041
|
-
/**
|
|
3042
|
-
* The asset's id in the source project, which the importing project reuses.
|
|
3043
|
-
*
|
|
3044
|
-
* Reusing it is what lets the rows that travelled alongside keep pointing at their files
|
|
3045
|
-
* without being rewritten, and it makes a second paste of the same clipboard a no-op: the id
|
|
3046
|
-
* is already in the library, so there is nothing to import.
|
|
3047
|
-
*/
|
|
3048
|
-
assetId: string;
|
|
3049
|
-
/** Author-facing file name, extension included: what the file is called once imported. */
|
|
3050
|
-
fileName: string;
|
|
3051
|
-
/**
|
|
3052
|
-
* The `AssetType` value as a string.
|
|
3053
|
-
*
|
|
3054
|
-
* Structural for the reason `assetSet.ts` gives for the same field: the enum lives under the
|
|
3055
|
-
* renderer and cannot be imported here.
|
|
3056
|
-
*/
|
|
3057
|
-
type: string;
|
|
3058
|
-
/** Size in bytes, when the offering side already knew it. */
|
|
3059
|
-
size?: number;
|
|
3060
|
-
/**
|
|
3061
|
-
* Whether the payload is a directory rather than a single file.
|
|
3062
|
-
*
|
|
3063
|
-
* True for model bundles, the one asset type whose contents are a tree. It decides both halves
|
|
3064
|
-
* of the transfer: the grant covering the entry reaches everything below the path, and the
|
|
3065
|
-
* importing project copies the tree instead of writing bytes.
|
|
3066
|
-
*/
|
|
3067
|
-
isDirectory?: boolean;
|
|
3068
|
-
}
|
|
3069
1739
|
declare const en: {
|
|
3070
1740
|
readonly common: {
|
|
3071
1741
|
readonly appName: "NarraLeaf Studio";
|
|
@@ -3152,6 +1822,16 @@ declare const en: {
|
|
|
3152
1822
|
readonly other: "{count} info";
|
|
3153
1823
|
};
|
|
3154
1824
|
};
|
|
1825
|
+
readonly flowCanvas: {
|
|
1826
|
+
readonly nodeHelp: "Press Enter or Space to select a node. Press Delete to remove it and Escape to cancel.";
|
|
1827
|
+
readonly movableNodeHelp: "Press Enter or Space to select a node, then move it with the arrow keys. Press Delete to remove it and Escape to cancel.";
|
|
1828
|
+
readonly wireHelp: "Press Enter or Space to select a wire. Press Delete to remove it and Escape to cancel.";
|
|
1829
|
+
readonly nodeMoved: "Moved the selected node {direction}. Position x {x}, y {y}";
|
|
1830
|
+
readonly up: "up";
|
|
1831
|
+
readonly down: "down";
|
|
1832
|
+
readonly left: "left";
|
|
1833
|
+
readonly right: "right";
|
|
1834
|
+
};
|
|
3155
1835
|
};
|
|
3156
1836
|
readonly menu: {
|
|
3157
1837
|
readonly app: {
|
|
@@ -3995,6 +2675,10 @@ declare const en: {
|
|
|
3995
2675
|
readonly characterSpeaker: "Character";
|
|
3996
2676
|
readonly scenesGroup: "Scenes";
|
|
3997
2677
|
readonly sceneSpeaker: "Scene";
|
|
2678
|
+
readonly endingsGroup: "Endings";
|
|
2679
|
+
readonly endingSpeaker: "Ending";
|
|
2680
|
+
readonly renamesGroup: "Renames";
|
|
2681
|
+
readonly renameSpeaker: "Rename";
|
|
3998
2682
|
readonly addKey: "Add";
|
|
3999
2683
|
readonly keyNamePlaceholder: "Key (menu.start\u2026)";
|
|
4000
2684
|
readonly keySourcePlaceholder: "Source text";
|
|
@@ -4037,6 +2721,24 @@ declare const en: {
|
|
|
4037
2721
|
readonly statusReviewed: "Reviewed";
|
|
4038
2722
|
readonly statusStale: "To review";
|
|
4039
2723
|
readonly findPlaceholder: "Find in translations";
|
|
2724
|
+
readonly orphans: {
|
|
2725
|
+
readonly source: "Orphan translations ({count})";
|
|
2726
|
+
readonly summary: {
|
|
2727
|
+
readonly one: "{count} translation belongs to a line that is not in the game";
|
|
2728
|
+
readonly other: "{count} translations belong to lines that are not in the game";
|
|
2729
|
+
};
|
|
2730
|
+
readonly none: "No orphan translations";
|
|
2731
|
+
readonly disabledLine: "Disabled line";
|
|
2732
|
+
readonly deletedLine: "Deleted line";
|
|
2733
|
+
readonly delete: "Delete";
|
|
2734
|
+
readonly deleteAll: "Delete all";
|
|
2735
|
+
readonly deleteConfirm: {
|
|
2736
|
+
readonly one: "Delete {count} orphan translation?";
|
|
2737
|
+
readonly other: "Delete {count} orphan translations?";
|
|
2738
|
+
};
|
|
2739
|
+
readonly deleteDetail: "This cannot be undone. A line that is enabled or restored later needs translating again.";
|
|
2740
|
+
readonly unknown: "A story cannot be read, so orphan translations are not listed.";
|
|
2741
|
+
};
|
|
4040
2742
|
};
|
|
4041
2743
|
readonly live: {
|
|
4042
2744
|
readonly entryClaimed: "{name} is translating this line";
|
|
@@ -4128,6 +2830,21 @@ declare const en: {
|
|
|
4128
2830
|
readonly notePlaceholder: "Note\u2026";
|
|
4129
2831
|
readonly dropHint: "Drop audio to assign";
|
|
4130
2832
|
readonly findPlaceholder: "Find in lines";
|
|
2833
|
+
readonly orphans: {
|
|
2834
|
+
readonly source: "Orphan voice ({count})";
|
|
2835
|
+
readonly summary: {
|
|
2836
|
+
readonly one: "{count} recording is linked to a line that is not in the game";
|
|
2837
|
+
readonly other: "{count} recordings are linked to lines that are not in the game";
|
|
2838
|
+
};
|
|
2839
|
+
readonly none: "No orphan voice";
|
|
2840
|
+
readonly removeAll: "Remove all";
|
|
2841
|
+
readonly removeConfirm: {
|
|
2842
|
+
readonly one: "Remove {count} orphan recording?";
|
|
2843
|
+
readonly other: "Remove {count} orphan recordings?";
|
|
2844
|
+
};
|
|
2845
|
+
readonly removeDetail: "The audio files stay in the asset library. A line that is enabled or restored later needs its audio assigned again.";
|
|
2846
|
+
readonly unknown: "A story cannot be read, so orphan voice is not listed.";
|
|
2847
|
+
};
|
|
4131
2848
|
};
|
|
4132
2849
|
};
|
|
4133
2850
|
readonly textSourceMigration: {
|
|
@@ -6086,8 +4803,12 @@ declare const en: {
|
|
|
6086
4803
|
readonly title: "Live Preview";
|
|
6087
4804
|
readonly dock: "Dock in the scene editor";
|
|
6088
4805
|
readonly pip: "Picture-in-picture";
|
|
4806
|
+
readonly skipTyping: "Skip typing animation";
|
|
6089
4807
|
readonly selectRow: "Select a story row to preview its stage state.";
|
|
6090
4808
|
readonly failed: "Preview failed";
|
|
4809
|
+
readonly settleTimeout: "The stage did not finish setting up in time.";
|
|
4810
|
+
readonly gameUiBlueprintError: "Game UI blueprint error: {message}";
|
|
4811
|
+
readonly previousSessionError: "Previous preview session: {message}";
|
|
6091
4812
|
readonly playFromHere: "Play from here";
|
|
6092
4813
|
readonly restart: "Restart";
|
|
6093
4814
|
readonly stop: "Stop playback";
|
|
@@ -6537,6 +5258,7 @@ declare const en: {
|
|
|
6537
5258
|
readonly parallel: "Parallel";
|
|
6538
5259
|
readonly race: "Race, first to finish";
|
|
6539
5260
|
readonly sequence: "In order";
|
|
5261
|
+
readonly noWait: "Doesn't wait";
|
|
6540
5262
|
readonly nvl: "NVL";
|
|
6541
5263
|
readonly menu: "Menu";
|
|
6542
5264
|
readonly option: "Option";
|
|
@@ -6623,6 +5345,7 @@ declare const en: {
|
|
|
6623
5345
|
readonly vfx: "{operation} ambience {name}";
|
|
6624
5346
|
readonly nvl: "NVL block";
|
|
6625
5347
|
readonly blueprint: "Blueprint";
|
|
5348
|
+
readonly blueprintNamed: "Blueprint {name}";
|
|
6626
5349
|
readonly pluginAction: "Plugin action";
|
|
6627
5350
|
readonly cameraOp: {
|
|
6628
5351
|
readonly transform: "Camera";
|
|
@@ -7222,6 +5945,7 @@ declare const en: {
|
|
|
7222
5945
|
};
|
|
7223
5946
|
readonly blueprint: {
|
|
7224
5947
|
readonly storyActionTitle: "Story Action";
|
|
5948
|
+
readonly name: "Blueprint name";
|
|
7225
5949
|
};
|
|
7226
5950
|
readonly audio: {
|
|
7227
5951
|
readonly track: "Track";
|
|
@@ -7327,13 +6051,9 @@ declare const en: {
|
|
|
7327
6051
|
readonly parallel: "Parallel";
|
|
7328
6052
|
readonly race: "Race";
|
|
7329
6053
|
readonly repeat: "Repeat";
|
|
7330
|
-
readonly
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
readonly all: "All";
|
|
7334
|
-
readonly allAsync: "All async";
|
|
7335
|
-
readonly any: "Any";
|
|
7336
|
-
};
|
|
6054
|
+
readonly rowsAfter: "Rows after";
|
|
6055
|
+
readonly rowsAfterWait: "Start when the group finishes";
|
|
6056
|
+
readonly rowsAfterStart: "Start with the group";
|
|
7337
6057
|
readonly times: "Times";
|
|
7338
6058
|
readonly loopKind: "Loop";
|
|
7339
6059
|
readonly loopKindTimes: "A number of times";
|
|
@@ -8082,6 +6802,34 @@ declare const en: {
|
|
|
8082
6802
|
readonly language: "Language";
|
|
8083
6803
|
readonly voiceLanguage: "Voice language";
|
|
8084
6804
|
};
|
|
6805
|
+
readonly valueType: {
|
|
6806
|
+
readonly inline: "{id}";
|
|
6807
|
+
readonly string: "String";
|
|
6808
|
+
readonly integer: "Integer";
|
|
6809
|
+
readonly float: "Float";
|
|
6810
|
+
readonly number: "Number";
|
|
6811
|
+
readonly boolean: "Boolean";
|
|
6812
|
+
readonly json: "JSON";
|
|
6813
|
+
readonly array: "Array";
|
|
6814
|
+
readonly list: "List";
|
|
6815
|
+
readonly any: "Any";
|
|
6816
|
+
readonly text: "Text";
|
|
6817
|
+
readonly audioTrack: "Audio track";
|
|
6818
|
+
readonly image: "Image";
|
|
6819
|
+
readonly imageAsset: "ImageAsset";
|
|
6820
|
+
readonly imageAssetOrNull: "ImageAsset|null";
|
|
6821
|
+
readonly color: "Color";
|
|
6822
|
+
readonly rgbaColor: "RGBAColor";
|
|
6823
|
+
readonly vector2D: "Vector2D";
|
|
6824
|
+
readonly rect: "Rect";
|
|
6825
|
+
readonly timer: "Timer";
|
|
6826
|
+
readonly animationToken: "AnimationToken";
|
|
6827
|
+
readonly soundHandle: "SoundHandle";
|
|
6828
|
+
readonly responseBody: "ResponseBody";
|
|
6829
|
+
readonly saveSlot: "SaveSlot";
|
|
6830
|
+
readonly element: "Element";
|
|
6831
|
+
readonly elementOf: "{widget} element";
|
|
6832
|
+
};
|
|
8085
6833
|
readonly diagnostics: {
|
|
8086
6834
|
readonly empty: "No diagnostics.";
|
|
8087
6835
|
readonly summary: "Messages \u00B7 {errors}E \u00B7 {warnings}W \u00B7 {infos}I";
|
|
@@ -8127,9 +6875,9 @@ declare const en: {
|
|
|
8127
6875
|
};
|
|
8128
6876
|
readonly graph: {
|
|
8129
6877
|
readonly noNodes: "Graph has no nodes yet.";
|
|
8130
|
-
readonly pinMultipleInput: "Pin {
|
|
8131
|
-
readonly pinMultipleOutput: "Pin {
|
|
8132
|
-
readonly entryMissingNode: "
|
|
6878
|
+
readonly pinMultipleInput: "Pin \"{pin}\" has multiple input connections; a pin takes one connection.";
|
|
6879
|
+
readonly pinMultipleOutput: "Pin \"{pin}\" has multiple output connections; a pin takes one connection.";
|
|
6880
|
+
readonly entryMissingNode: "The entry points to a node that does not exist.";
|
|
8133
6881
|
readonly entryInvalid: "Function entry not found.";
|
|
8134
6882
|
};
|
|
8135
6883
|
readonly event: {
|
|
@@ -8142,21 +6890,21 @@ declare const en: {
|
|
|
8142
6890
|
};
|
|
8143
6891
|
readonly edge: {
|
|
8144
6892
|
readonly selfConnection: "Node \"{node}\" cannot connect to itself.";
|
|
8145
|
-
readonly fromUnknown: "
|
|
8146
|
-
readonly toUnknown: "
|
|
6893
|
+
readonly fromUnknown: "A wire starts at a node that does not exist.";
|
|
6894
|
+
readonly toUnknown: "A wire ends at a node that does not exist.";
|
|
8147
6895
|
readonly portMismatch: "Pin mismatch on {from} \u2192 {to}.";
|
|
8148
6896
|
readonly connectionInvalid: "Invalid connection {from} -> {to}.{detail}";
|
|
8149
6897
|
readonly connectionTypeDetail: " Type mismatch: {from} -> {to}.";
|
|
8150
6898
|
};
|
|
8151
6899
|
readonly node: {
|
|
8152
|
-
readonly contextInvalid: "Node \"{name}\" is not allowed in
|
|
6900
|
+
readonly contextInvalid: "Node \"{name}\" is not allowed in {owner}.{hint}";
|
|
8153
6901
|
readonly contextValueReturnHint: " Return Value only belongs in Blueprint Value graphs.";
|
|
8154
6902
|
readonly contextListItemHint: " It reads the list row it runs for, and nothing here draws one.";
|
|
8155
|
-
readonly noRuntime: "Node \"{node}\"
|
|
6903
|
+
readonly noRuntime: "Node \"{node}\" has no runtime.";
|
|
8156
6904
|
readonly inputMissing: "\"{node}\" has nothing connected to \"{pin}\".";
|
|
8157
6905
|
readonly stepLimit: "\"{head}\" stopped at \"{node}\" after {steps} steps without a wait.";
|
|
8158
6906
|
readonly stepLimitGlobal: "\"{head}\" in App logic stopped at \"{node}\" after {steps} steps without a wait.";
|
|
8159
|
-
readonly unknownType: "
|
|
6907
|
+
readonly unknownType: "Unknown node type \"{type}\". Its plugin may be uninstalled or disabled.";
|
|
8160
6908
|
readonly variableIdInvalid: "Node \"{node}\": pick a variable.";
|
|
8161
6909
|
readonly persistentVariableIdInvalid: "Node \"{node}\": pick a persistent variable.";
|
|
8162
6910
|
readonly savedVariableIdInvalid: "Node \"{node}\": pick a saved variable.";
|
|
@@ -8167,11 +6915,11 @@ declare const en: {
|
|
|
8167
6915
|
readonly pageParamMissing: "\"{node}\": the page does not declare this param.";
|
|
8168
6916
|
};
|
|
8169
6917
|
readonly fn: {
|
|
8170
|
-
readonly nameMissing: "
|
|
6918
|
+
readonly nameMissing: "\"{node}\": set a function name.";
|
|
8171
6919
|
readonly duplicateName: "Fn name \"{name}\" is used by another function in scope.";
|
|
8172
6920
|
readonly returnOrphan: "Fn Return must be reachable from a Fn head.";
|
|
8173
6921
|
readonly returnMultipleHeads: "Fn Return is reachable from multiple Fn heads.";
|
|
8174
|
-
readonly returnSignatureConflict: "
|
|
6922
|
+
readonly returnSignatureConflict: "\"{node}\" declares different results than the first Return of this fn.";
|
|
8175
6923
|
readonly callUnset: "Node \"{node}\": pick a function.";
|
|
8176
6924
|
readonly callTargetNotFound: "Fn \"{name}\" does not exist in this scope.";
|
|
8177
6925
|
readonly callSignatureStale: "Function signature changed; re-select \"{name}\" to update its pins.";
|
|
@@ -8399,6 +7147,10 @@ declare const en: {
|
|
|
8399
7147
|
readonly rawJson: "Raw JSON";
|
|
8400
7148
|
readonly root: "Root";
|
|
8401
7149
|
readonly edit: "Edit JSON";
|
|
7150
|
+
readonly itemCount: {
|
|
7151
|
+
readonly one: "{count} item";
|
|
7152
|
+
readonly other: "{count} items";
|
|
7153
|
+
};
|
|
8402
7154
|
};
|
|
8403
7155
|
readonly audio: {
|
|
8404
7156
|
readonly select: "Select audio";
|
|
@@ -8438,6 +7190,8 @@ declare const en: {
|
|
|
8438
7190
|
readonly removeOutput: "Remove output pin";
|
|
8439
7191
|
readonly jsonFieldName: "JSON object field name";
|
|
8440
7192
|
readonly readsRow: "Current row";
|
|
7193
|
+
readonly newParamName: "param{n}";
|
|
7194
|
+
readonly newResultName: "result{n}";
|
|
8441
7195
|
};
|
|
8442
7196
|
readonly comment: {
|
|
8443
7197
|
readonly color: {
|
|
@@ -9937,8 +8691,8 @@ declare const en: {
|
|
|
9937
8691
|
readonly namePlaceholder: "Name";
|
|
9938
8692
|
readonly defaultPlaceholder: "Default";
|
|
9939
8693
|
readonly type: "Type";
|
|
9940
|
-
readonly
|
|
9941
|
-
readonly
|
|
8694
|
+
readonly trackMissing: "Missing track";
|
|
8695
|
+
readonly trackNone: "No track";
|
|
9942
8696
|
readonly bindLabel: "Parameter";
|
|
9943
8697
|
readonly bindNone: "No parameter";
|
|
9944
8698
|
readonly bindMissing: "Missing parameter";
|
|
@@ -9957,12 +8711,6 @@ declare const en: {
|
|
|
9957
8711
|
readonly namePlaceholder: "Name";
|
|
9958
8712
|
readonly default: "Default";
|
|
9959
8713
|
readonly type: "Type";
|
|
9960
|
-
readonly typeString: "String";
|
|
9961
|
-
readonly typeText: "Text";
|
|
9962
|
-
readonly typeNumber: "Number";
|
|
9963
|
-
readonly typeBoolean: "Boolean";
|
|
9964
|
-
readonly typeList: "List";
|
|
9965
|
-
readonly typeJson: "JSON";
|
|
9966
8714
|
};
|
|
9967
8715
|
readonly binding: {
|
|
9968
8716
|
readonly notReady: "Blueprint not ready for this control.";
|
|
@@ -10046,6 +8794,7 @@ declare const en: {
|
|
|
10046
8794
|
readonly unknown: "Usage could not be determined";
|
|
10047
8795
|
readonly unknownDetail: "{location} could not be read";
|
|
10048
8796
|
readonly unknownComputed: "{location} picks its asset by a name assembled at run time";
|
|
8797
|
+
readonly defaultFonts: "Default fonts";
|
|
10049
8798
|
readonly count: {
|
|
10050
8799
|
readonly one: "{count} reference";
|
|
10051
8800
|
readonly other: "{count} references";
|
|
@@ -10343,14 +9092,6 @@ declare const en: {
|
|
|
10343
9092
|
readonly saveSchema: {
|
|
10344
9093
|
readonly title: "Save fields";
|
|
10345
9094
|
readonly open: "Save fields";
|
|
10346
|
-
readonly type: {
|
|
10347
|
-
readonly string: "String";
|
|
10348
|
-
readonly integer: "Integer";
|
|
10349
|
-
readonly float: "Float";
|
|
10350
|
-
readonly boolean: "Boolean";
|
|
10351
|
-
readonly json: "JSON";
|
|
10352
|
-
readonly array: "Array";
|
|
10353
|
-
};
|
|
10354
9095
|
readonly field: {
|
|
10355
9096
|
readonly name: "Name";
|
|
10356
9097
|
readonly type: "Type";
|
|
@@ -10362,14 +9103,6 @@ declare const en: {
|
|
|
10362
9103
|
};
|
|
10363
9104
|
};
|
|
10364
9105
|
readonly struct: {
|
|
10365
|
-
readonly type: {
|
|
10366
|
-
readonly string: "String";
|
|
10367
|
-
readonly number: "Number";
|
|
10368
|
-
readonly boolean: "Boolean";
|
|
10369
|
-
readonly image: "Image";
|
|
10370
|
-
readonly color: "Color";
|
|
10371
|
-
readonly json: "JSON";
|
|
10372
|
-
};
|
|
10373
9106
|
readonly field: {
|
|
10374
9107
|
readonly name: "Name";
|
|
10375
9108
|
readonly type: "Type";
|
|
@@ -10403,18 +9136,13 @@ declare const en: {
|
|
|
10403
9136
|
};
|
|
10404
9137
|
};
|
|
10405
9138
|
readonly storyVars: {
|
|
10406
|
-
readonly valueType: {
|
|
10407
|
-
readonly boolean: "Boolean";
|
|
10408
|
-
readonly number: "Number";
|
|
10409
|
-
readonly string: "String";
|
|
10410
|
-
readonly json: "JSON";
|
|
10411
|
-
};
|
|
10412
9139
|
readonly value: {
|
|
10413
9140
|
readonly true: "True";
|
|
10414
9141
|
readonly false: "False";
|
|
10415
9142
|
};
|
|
10416
9143
|
readonly row: {
|
|
10417
9144
|
readonly nameAria: "Variable name";
|
|
9145
|
+
readonly defaultName: "Variable";
|
|
10418
9146
|
readonly defaultPlaceholder: "default";
|
|
10419
9147
|
readonly defaultAria: "Default value";
|
|
10420
9148
|
readonly delete: "Delete variable";
|
|
@@ -10627,6 +9355,9 @@ declare const en: {
|
|
|
10627
9355
|
readonly canvas: {
|
|
10628
9356
|
readonly unknownWidget: "Unknown widget";
|
|
10629
9357
|
readonly widgetRenderFailed: "Widget failed to draw";
|
|
9358
|
+
readonly componentMissing: "Missing component";
|
|
9359
|
+
readonly componentLoopBlocked: "Component loop blocked";
|
|
9360
|
+
readonly componentRootMissing: "Component root missing";
|
|
10630
9361
|
readonly wordsFromRowField: "These words come from the list row field \u201C{field}\u201D.";
|
|
10631
9362
|
readonly wordsFromBlueprintValue: "These words come from the blueprint value \u201C{name}\u201D.";
|
|
10632
9363
|
readonly wordsFromComponentParam: "These words come from the component parameter \u201C{param}\u201D, set on each placement.";
|
|
@@ -11814,6 +10545,8 @@ declare const en: {
|
|
|
11814
10545
|
readonly ending: {
|
|
11815
10546
|
readonly title: "Page shown when the story ends";
|
|
11816
10547
|
readonly none: "Show nothing";
|
|
10548
|
+
readonly unset: "Not chosen";
|
|
10549
|
+
readonly missing: "Deleted page";
|
|
11817
10550
|
};
|
|
11818
10551
|
readonly usedBy: {
|
|
11819
10552
|
readonly one: "Used by {count} reference";
|
|
@@ -12017,6 +10750,11 @@ declare const en: {
|
|
|
12017
10750
|
readonly one: "{count} track under it moves to {parent}.";
|
|
12018
10751
|
readonly other: "{count} tracks under it move to {parent}.";
|
|
12019
10752
|
};
|
|
10753
|
+
readonly builtinName: {
|
|
10754
|
+
readonly bgm: "Music";
|
|
10755
|
+
readonly sound: "SFX";
|
|
10756
|
+
readonly voice: "Voice";
|
|
10757
|
+
};
|
|
12020
10758
|
readonly slider: {
|
|
12021
10759
|
readonly bgm: "Music volume";
|
|
12022
10760
|
readonly sound: "SFX volume";
|
|
@@ -13394,6 +12132,7 @@ declare const en: {
|
|
|
13394
12132
|
readonly contentLabel: "Variant the content comes from";
|
|
13395
12133
|
readonly contentHint: "The scenes, variant conditions and art of this variant go into the patch.";
|
|
13396
12134
|
readonly sameVariant: "The content and the build it updates are the same variant. The patch carries no changes.";
|
|
12135
|
+
readonly sameProject: "The build it updates is made from the current project. The patch carries no changes. For a build already released, choose An existing build folder.";
|
|
13397
12136
|
readonly outputLabel: "Save as";
|
|
13398
12137
|
readonly nameLabel: "Name";
|
|
13399
12138
|
readonly namePlaceholder: "Shown in the game's log";
|
|
@@ -14352,6 +13091,7 @@ declare const en: {
|
|
|
14352
13091
|
};
|
|
14353
13092
|
readonly message: {
|
|
14354
13093
|
readonly ruleFailed: "{rule} could not run";
|
|
13094
|
+
readonly listSeparator: ", ";
|
|
14355
13095
|
readonly storyLoadFailed: "{story} could not be opened";
|
|
14356
13096
|
readonly storyTooOld: "{story} is in story format v{version}, and this Studio opens v{minimum} and later";
|
|
14357
13097
|
readonly storyTooNew: "{story} was written by a newer NarraLeaf Studio (story format v{version}); this Studio reads up to v{supported}";
|
|
@@ -15238,13 +13978,14 @@ declare const en: {
|
|
|
15238
13978
|
readonly finding: {
|
|
15239
13979
|
readonly sceneUnreachable: "No path can satisfy the conditions leading to \"{scene}\"";
|
|
15240
13980
|
readonly optionUnreachable: "No path can satisfy the condition for offering \"{option}\"";
|
|
13981
|
+
readonly conditionUnreachable: "No path can satisfy \"{condition}\"";
|
|
15241
13982
|
readonly branchUnreachable: "No path can satisfy the condition for taking this branch";
|
|
15242
13983
|
readonly endingUnreachable: "\"{name}\" is written but no path can satisfy the conditions to reach it";
|
|
15243
13984
|
readonly endingUnreachableUnnamed: "This ending is written but no path can satisfy the conditions to reach it";
|
|
15244
13985
|
};
|
|
15245
13986
|
readonly summary: {
|
|
15246
13987
|
readonly passed: "Everything the script leads to can be reached";
|
|
15247
|
-
readonly failed: "Unreachable: scenes {scenes}, options {options}, endings {endings}";
|
|
13988
|
+
readonly failed: "Unreachable: scenes {scenes}, options {options}, condition branches {conditions}, endings {endings}";
|
|
15248
13989
|
};
|
|
15249
13990
|
};
|
|
15250
13991
|
readonly reachableEndings: {
|
|
@@ -15595,15 +14336,27 @@ declare const en: {
|
|
|
15595
14336
|
readonly checking: "Checking for updates\u2026";
|
|
15596
14337
|
readonly available: "Version {version} is available.";
|
|
15597
14338
|
readonly downloading: "Downloading version {version}\u2026";
|
|
14339
|
+
readonly preparing: "Preparing version {version}\u2026";
|
|
15598
14340
|
readonly ready: "Version {version} is ready to install.";
|
|
14341
|
+
readonly readyFast: "Version {version} is ready.";
|
|
15599
14342
|
readonly error: "Could not check for updates.";
|
|
14343
|
+
readonly failed: "Version {version} could not be downloaded.";
|
|
15600
14344
|
readonly manual: "Version {version} is available to download.";
|
|
15601
14345
|
};
|
|
14346
|
+
readonly errors: {
|
|
14347
|
+
readonly connection: "Could not reach the download server ({reason}).";
|
|
14348
|
+
};
|
|
15602
14349
|
readonly versions: "Installed {current}";
|
|
14350
|
+
readonly readyHint: "Also applied when Studio quits.";
|
|
14351
|
+
readonly indicator: {
|
|
14352
|
+
readonly label: "Software update";
|
|
14353
|
+
};
|
|
15603
14354
|
readonly actions: {
|
|
15604
14355
|
readonly check: "Check for Updates";
|
|
15605
14356
|
readonly download: "Download Update";
|
|
15606
14357
|
readonly install: "Restart and Install";
|
|
14358
|
+
readonly restart: "Restart to Update";
|
|
14359
|
+
readonly cancel: "Cancel Update";
|
|
15607
14360
|
readonly releaseNotes: "Release notes";
|
|
15608
14361
|
readonly openDownloadPage: "Open download page";
|
|
15609
14362
|
};
|
|
@@ -15614,17 +14367,25 @@ declare const en: {
|
|
|
15614
14367
|
};
|
|
15615
14368
|
readonly setting: {
|
|
15616
14369
|
readonly checkOnLaunch: {
|
|
15617
|
-
readonly label: "Check for updates
|
|
15618
|
-
readonly description: "Asks GitHub
|
|
14370
|
+
readonly label: "Check for updates automatically";
|
|
14371
|
+
readonly description: "Asks GitHub shortly after Studio starts, and every few hours while it runs.";
|
|
14372
|
+
};
|
|
14373
|
+
readonly autoDownload: {
|
|
14374
|
+
readonly label: "Download updates automatically";
|
|
14375
|
+
readonly description: "A new version is downloaded and prepared in the background. Restarting Studio applies it.";
|
|
15619
14376
|
};
|
|
15620
14377
|
};
|
|
15621
14378
|
readonly notification: {
|
|
15622
14379
|
readonly message: "NarraLeaf Studio {version} is available";
|
|
15623
14380
|
readonly detail: "You are running {current}.";
|
|
15624
14381
|
readonly action: "View update";
|
|
14382
|
+
readonly readyMessage: "NarraLeaf Studio {version} is ready";
|
|
14383
|
+
readonly readyDetail: "Restart to finish updating.";
|
|
15625
14384
|
};
|
|
15626
14385
|
readonly launcher: {
|
|
15627
14386
|
readonly available: "Update to {version}";
|
|
14387
|
+
readonly progress: "Updating to {version}\u2026";
|
|
14388
|
+
readonly ready: "Restart to update to {version}";
|
|
15628
14389
|
};
|
|
15629
14390
|
readonly quitPrompt: {
|
|
15630
14391
|
readonly title: "Update in progress";
|
|
@@ -15677,6 +14438,19 @@ declare const en: {
|
|
|
15677
14438
|
};
|
|
15678
14439
|
readonly preview: "Preview in";
|
|
15679
14440
|
readonly excluded: "Not used for {language}";
|
|
14441
|
+
readonly builtin: {
|
|
14442
|
+
readonly group: "Built-in fonts";
|
|
14443
|
+
readonly systemUi: "System UI";
|
|
14444
|
+
readonly sansSerif: "Sans-serif (generic)";
|
|
14445
|
+
readonly serif: "Serif (generic)";
|
|
14446
|
+
readonly monospace: "Monospace (generic)";
|
|
14447
|
+
readonly kind: {
|
|
14448
|
+
readonly system: "System font";
|
|
14449
|
+
readonly sansSerif: "Sans-serif";
|
|
14450
|
+
readonly serif: "Serif";
|
|
14451
|
+
readonly monospace: "Monospace";
|
|
14452
|
+
};
|
|
14453
|
+
};
|
|
15680
14454
|
};
|
|
15681
14455
|
readonly panel: {
|
|
15682
14456
|
readonly add: "Add color";
|
|
@@ -15804,6 +14578,10 @@ declare const en: {
|
|
|
15804
14578
|
readonly detail: "It is running in another browser tab. Close that tab, then reload this page.";
|
|
15805
14579
|
readonly reload: "Reload";
|
|
15806
14580
|
};
|
|
14581
|
+
readonly words: {
|
|
14582
|
+
readonly option: "Option";
|
|
14583
|
+
readonly unknownSpeaker: "Unknown";
|
|
14584
|
+
};
|
|
15807
14585
|
};
|
|
15808
14586
|
readonly crash: {
|
|
15809
14587
|
readonly screen: {
|
|
@@ -15846,36 +14624,1366 @@ declare const en: {
|
|
|
15846
14624
|
readonly quit: "Quit";
|
|
15847
14625
|
};
|
|
15848
14626
|
};
|
|
15849
|
-
readonly documents: {
|
|
15850
|
-
readonly tooNew: {
|
|
15851
|
-
readonly message: "{subject} was written by a newer NarraLeaf Studio ({kind} format v{version}); this build reads up to v{supported}";
|
|
15852
|
-
readonly kind: {
|
|
15853
|
-
readonly story: "story";
|
|
15854
|
-
readonly storyIndex: "story library";
|
|
15855
|
-
readonly storyAnimation: "animation";
|
|
15856
|
-
readonly uiDocument: "interface";
|
|
15857
|
-
readonly uiGraphs: "interface blueprint";
|
|
15858
|
-
readonly blueprints: "blueprint";
|
|
15859
|
-
readonly variables: "variable";
|
|
15860
|
-
readonly saveSchema: "save schema";
|
|
15861
|
-
readonly localization: "translation";
|
|
15862
|
-
readonly localizationKeys: "translation key";
|
|
15863
|
-
readonly voice: "voice";
|
|
15864
|
-
readonly brand: "design";
|
|
15865
|
-
readonly appTags: "variant";
|
|
15866
|
-
readonly dlc: "DLC";
|
|
15867
|
-
readonly assetSets: "asset set";
|
|
15868
|
-
readonly audioTracks: "audio track";
|
|
15869
|
-
readonly characters: "character";
|
|
15870
|
-
};
|
|
15871
|
-
};
|
|
14627
|
+
readonly documents: {
|
|
14628
|
+
readonly tooNew: {
|
|
14629
|
+
readonly message: "{subject} was written by a newer NarraLeaf Studio ({kind} format v{version}); this build reads up to v{supported}";
|
|
14630
|
+
readonly kind: {
|
|
14631
|
+
readonly story: "story";
|
|
14632
|
+
readonly storyIndex: "story library";
|
|
14633
|
+
readonly storyAnimation: "animation";
|
|
14634
|
+
readonly uiDocument: "interface";
|
|
14635
|
+
readonly uiGraphs: "interface blueprint";
|
|
14636
|
+
readonly blueprints: "blueprint";
|
|
14637
|
+
readonly variables: "variable";
|
|
14638
|
+
readonly saveSchema: "save schema";
|
|
14639
|
+
readonly localization: "translation";
|
|
14640
|
+
readonly localizationKeys: "translation key";
|
|
14641
|
+
readonly voice: "voice";
|
|
14642
|
+
readonly brand: "design";
|
|
14643
|
+
readonly appTags: "variant";
|
|
14644
|
+
readonly dlc: "DLC";
|
|
14645
|
+
readonly assetSets: "asset set";
|
|
14646
|
+
readonly audioTracks: "audio track";
|
|
14647
|
+
readonly characters: "character";
|
|
14648
|
+
};
|
|
14649
|
+
};
|
|
14650
|
+
};
|
|
14651
|
+
};
|
|
14652
|
+
type Messages = typeof en;
|
|
14653
|
+
type Flatten<T, Prefix extends string = ""> = {
|
|
14654
|
+
[K in keyof T & string]: T[K] extends string ? `${Prefix}${K}` : Flatten<T[K], `${Prefix}${K}.`>;
|
|
14655
|
+
}[keyof T & string];
|
|
14656
|
+
type TranslationKey = Flatten<Messages> | (string & {});
|
|
14657
|
+
type AudioClipRegion = {
|
|
14658
|
+
/** Offset from the start of the clip, in milliseconds. Where playback starts. */
|
|
14659
|
+
inMs?: number;
|
|
14660
|
+
/** Offset from the start of the clip, in milliseconds. Where playback stops, or where a loop turns around. */
|
|
14661
|
+
outMs?: number;
|
|
14662
|
+
/**
|
|
14663
|
+
* Offset from the start of the clip, in milliseconds. Where each repeat returns to.
|
|
14664
|
+
*
|
|
14665
|
+
* Absent means "return to {@link inMs}" - a plain loop, and exactly what every record written
|
|
14666
|
+
* before this field existed means. Set past the in point it describes the standard VN
|
|
14667
|
+
* **intro→loop**: `inMs..loopStartMs` plays once, then `loopStartMs..outMs` repeats forever.
|
|
14668
|
+
*
|
|
14669
|
+
* Constrained to `inMs <= loopStartMs < outMs`. A value outside that window is dropped rather
|
|
14670
|
+
* than clamped: clamping would invent a loop point the author never marked, while dropping
|
|
14671
|
+
* degrades to the plain loop the two other markers already describe.
|
|
14672
|
+
*/
|
|
14673
|
+
loopStartMs?: number;
|
|
14674
|
+
/**
|
|
14675
|
+
* Playback gain in decibels, never above 0: the engine cannot raise a clip past its own level, so
|
|
14676
|
+
* balancing loudness means bringing the loud clips down. Absent is unity. Folded into a volume
|
|
14677
|
+
* only by {@link clipVolume}.
|
|
14678
|
+
*/
|
|
14679
|
+
gainDb?: number;
|
|
14680
|
+
};
|
|
14681
|
+
type StoredAudioClipRegion = Pick<AudioClipRegion, "inMs" | "outMs" | "loopStartMs">;
|
|
14682
|
+
type StoredAudioGain = {
|
|
14683
|
+
db: number;
|
|
14684
|
+
};
|
|
14685
|
+
type BlueprintOpenExternalRequest = {
|
|
14686
|
+
url: string;
|
|
14687
|
+
};
|
|
14688
|
+
type BlueprintOpenExternalOutcome = "opened" | "refused" | "failed";
|
|
14689
|
+
type BlueprintOpenExternalResult = {
|
|
14690
|
+
outcome: BlueprintOpenExternalOutcome;
|
|
14691
|
+
/** Human-readable reason, null when the page was handed over. */
|
|
14692
|
+
error: string | null;
|
|
14693
|
+
};
|
|
14694
|
+
type BlueprintGraphKind = "event" | "function" | "macro";
|
|
14695
|
+
type GameBuildPlatform = "windows" | "macos" | "linux" | "web" | "android" | "ios";
|
|
14696
|
+
declare const UI_STRUCT_FIELD_TYPES: readonly [
|
|
14697
|
+
"string",
|
|
14698
|
+
"number",
|
|
14699
|
+
"boolean",
|
|
14700
|
+
"image",
|
|
14701
|
+
"color",
|
|
14702
|
+
"json"
|
|
14703
|
+
];
|
|
14704
|
+
type UIStructFieldType = (typeof UI_STRUCT_FIELD_TYPES)[number];
|
|
14705
|
+
type UIStructField = {
|
|
14706
|
+
/**
|
|
14707
|
+
* Stable identity. Value bindings, node params and generated pins all address a field by this,
|
|
14708
|
+
* so renaming `key` costs nothing and reshuffling the list costs nothing.
|
|
14709
|
+
*/
|
|
14710
|
+
id: string;
|
|
14711
|
+
/** The property name this field occupies inside an item object. */
|
|
14712
|
+
key: string;
|
|
14713
|
+
/**
|
|
14714
|
+
* What the author called it, shown in the item table header and in every field picker.
|
|
14715
|
+
*
|
|
14716
|
+
* Optional because `key` is usually already the readable name; a label is what lets a field
|
|
14717
|
+
* whose key has to stay ASCII (it travels in JSON to a plugin, a save file, a story variable)
|
|
14718
|
+
* still read as words in the editor.
|
|
14719
|
+
*/
|
|
14720
|
+
label?: string;
|
|
14721
|
+
type: UIStructFieldType;
|
|
14722
|
+
};
|
|
14723
|
+
type UIStructId = string;
|
|
14724
|
+
type UIStructDef = {
|
|
14725
|
+
id: UIStructId;
|
|
14726
|
+
fields: UIStructField[];
|
|
14727
|
+
};
|
|
14728
|
+
type PluginPermissionPersistence = "temporary" | "permanent";
|
|
14729
|
+
type PluginFileSystemPermissionMode = "read" | "write" | "readwrite";
|
|
14730
|
+
/**
|
|
14731
|
+
* Install-time permissions split into two families, and the split is the whole
|
|
14732
|
+
* point:
|
|
14733
|
+
*
|
|
14734
|
+
* - **Author-declared** (`filesystem`, `api`) — privileged Studio controls the
|
|
14735
|
+
* plugin asks for explicitly in `permissions[]`.
|
|
14736
|
+
* - **Derived** (`runtime`, `sidecar`, `buildDependency`, `externalLink`,
|
|
14737
|
+
* `network`) — computed from `contributes` by {@link validatePluginManifest}
|
|
14738
|
+
* and *rejected* if written by
|
|
14739
|
+
* hand. A capability is declared in exactly one place, so what the prompt shows
|
|
14740
|
+
* and what the plugin can actually reach cannot drift apart. Adding a
|
|
14741
|
+
* capability widens the permission set, which makes the update re-prompt for
|
|
14742
|
+
* free (see `isPermissionSubset`).
|
|
14743
|
+
*/
|
|
14744
|
+
export type PluginInstallPermission = {
|
|
14745
|
+
kind: "filesystem";
|
|
14746
|
+
path: string;
|
|
14747
|
+
mode: PluginFileSystemPermissionMode;
|
|
14748
|
+
recursive: boolean;
|
|
14749
|
+
} | {
|
|
14750
|
+
kind: "api";
|
|
14751
|
+
capability: string;
|
|
14752
|
+
} | {
|
|
14753
|
+
/** Derived from `contributes.runtimeCapabilities`. */
|
|
14754
|
+
kind: "runtime";
|
|
14755
|
+
capability: PluginRuntimeCapability;
|
|
14756
|
+
} | {
|
|
14757
|
+
/** Derived from `contributes.sidecars`: a native child process shipped inside the author's game. */
|
|
14758
|
+
kind: "sidecar";
|
|
14759
|
+
id: string;
|
|
14760
|
+
/**
|
|
14761
|
+
* Which of the two sidecar shapes this is, carried through from the manifest because the
|
|
14762
|
+
* two are different promises: `executable` starts a separate binary, `node` runs the
|
|
14763
|
+
* plugin's own JavaScript under the game's Electron as Node. An author told only "this
|
|
14764
|
+
* ships a helper program" has not been told which one they are approving.
|
|
14765
|
+
*
|
|
14766
|
+
* Named apart from `kind` above on purpose - that one says which permission this is, this
|
|
14767
|
+
* one says what the sidecar runs.
|
|
14768
|
+
*/
|
|
14769
|
+
sidecarKind: PluginSidecarKind;
|
|
14770
|
+
/** `<platform>-<arch>` keys the sidecar ships binaries for. */
|
|
14771
|
+
platforms: string[];
|
|
14772
|
+
} | {
|
|
14773
|
+
/** Derived from `contributes.buildDependencies`: binaries downloaded at build time. */
|
|
14774
|
+
kind: "buildDependency";
|
|
14775
|
+
id: string;
|
|
14776
|
+
/** Distinct hostnames the binaries are fetched from. */
|
|
14777
|
+
hosts: string[];
|
|
14778
|
+
} | {
|
|
14779
|
+
/**
|
|
14780
|
+
* Derived from `contributes.externalLinks`: addresses outside the game this plugin may open
|
|
14781
|
+
* in the player's browser or platform handler.
|
|
14782
|
+
*
|
|
14783
|
+
* One permission carrying every pattern rather than one permission each, because what the
|
|
14784
|
+
* author is deciding is a single question - "may this plugin send the player out to these
|
|
14785
|
+
* places" - and a prompt that asked it four times would be four chances to stop reading.
|
|
14786
|
+
* `isPermissionSubset` still compares the patterns one by one, so adding one to a later
|
|
14787
|
+
* version widens the set and re-prompts.
|
|
14788
|
+
*
|
|
14789
|
+
* The patterns are the author's own strings, unrewritten: this list is what the prompt
|
|
14790
|
+
* shows, and normalizing a permission before showing it to the person approving it would
|
|
14791
|
+
* make the prompt and the manifest two slightly different documents.
|
|
14792
|
+
*/
|
|
14793
|
+
kind: "externalLink";
|
|
14794
|
+
patterns: string[];
|
|
14795
|
+
} | {
|
|
14796
|
+
/**
|
|
14797
|
+
* Derived from `contributes.network`: hosts this plugin's runtime code requests bytes from.
|
|
14798
|
+
*
|
|
14799
|
+
* A separate question from `externalLink` and never folded into it. Opening a page hands an
|
|
14800
|
+
* address to the browser the player already uses and nothing comes back; this fetches, and
|
|
14801
|
+
* what comes back runs inside the game. An author deciding about one has not decided about
|
|
14802
|
+
* the other.
|
|
14803
|
+
*
|
|
14804
|
+
* The patterns are also what a project's network allowlist shows attributed to this plugin,
|
|
14805
|
+
* which is the point of declaring them: a build narrowed to a list still reaches what the
|
|
14806
|
+
* author approved here, and the list can say so rather than silently making room.
|
|
14807
|
+
*/
|
|
14808
|
+
kind: "network";
|
|
14809
|
+
patterns: string[];
|
|
14810
|
+
};
|
|
14811
|
+
type PluginSidecarKind = "executable" | "node";
|
|
14812
|
+
declare const PluginRuntimeCapability: {
|
|
14813
|
+
/** `app.game.store` — plugin-scoped persistent key/value storage. */
|
|
14814
|
+
readonly Store: "store";
|
|
14815
|
+
/** `app.game.events` — game lifecycle and engine event subscription. */
|
|
14816
|
+
readonly Events: "events";
|
|
14817
|
+
/** `app.game.state.get` — read story variables. */
|
|
14818
|
+
readonly StateRead: "state.read";
|
|
14819
|
+
/** `app.game.state.set` — write story variables. */
|
|
14820
|
+
readonly StateWrite: "state.write";
|
|
14821
|
+
/** `app.game.saves` — list and read save metadata. */
|
|
14822
|
+
readonly SavesRead: "saves.read";
|
|
14823
|
+
/**
|
|
14824
|
+
* `app.game.saves.write` / `.load` — overwrite a save slot and load one.
|
|
14825
|
+
*
|
|
14826
|
+
* Separate from (and heavier than) `saves.read`: this can destroy a
|
|
14827
|
+
* playthrough. It exists because quick-save/quick-load is an ordinary thing
|
|
14828
|
+
* for a visual novel to ship — the built-in Quick Save plugin is exactly
|
|
14829
|
+
* this — and a capability model that cannot express a feature the product
|
|
14830
|
+
* already has would just push authors back around it.
|
|
14831
|
+
*/
|
|
14832
|
+
readonly SavesWrite: "saves.write";
|
|
14833
|
+
/** `app.game.ui.overlay` — draw on top of the game. */
|
|
14834
|
+
readonly UiOverlay: "ui.overlay";
|
|
14835
|
+
/** `app.game.assets` — resolve packaged asset URLs. */
|
|
14836
|
+
readonly Assets: "assets";
|
|
14837
|
+
/**
|
|
14838
|
+
* `app.game.locale` — read and observe the game language, and read the project's own
|
|
14839
|
+
* localized text through the same table and fallback chain the `Get Text` node uses.
|
|
14840
|
+
*/
|
|
14841
|
+
readonly Locale: "locale";
|
|
14842
|
+
/**
|
|
14843
|
+
* `app.game.menu` — own the menu bar above the game.
|
|
14844
|
+
*
|
|
14845
|
+
* Its own capability rather than part of anything else, because what it grants is *the chrome
|
|
14846
|
+
* the player reads the game through*: every row on that bar, and what each one does, is this
|
|
14847
|
+
* plugin's to decide. The rows still act through the game's own vocabulary (see
|
|
14848
|
+
* `@shared/types/gameMenu`) - nothing here reaches past what a blueprint could already do - but
|
|
14849
|
+
* an author approving this is handing over a surface their players will read as the game's own,
|
|
14850
|
+
* which is worth a name at install rather than arriving with something else.
|
|
14851
|
+
*
|
|
14852
|
+
* Absent on shells with no bar to give (the web export, Dev Mode), which is reported the way
|
|
14853
|
+
* every environment difference is: the member is not there.
|
|
14854
|
+
*/
|
|
14855
|
+
readonly Menu: "menu";
|
|
14856
|
+
/**
|
|
14857
|
+
* `app.game.story` — register a compile pass that observes each scene and injects engine
|
|
14858
|
+
* actions around its rows.
|
|
14859
|
+
*
|
|
14860
|
+
* The heaviest of the ten to read, and the one whose weight is easiest to miss: nothing here
|
|
14861
|
+
* touches the player's files, and the author still sees only their own document — but a pass
|
|
14862
|
+
* runs over *every* scene of the project, sees who speaks in each, and can put actions around
|
|
14863
|
+
* lines it did not write. That is a plugin editing the story, at the last moment before it
|
|
14864
|
+
* plays, and it is worth a name the author approves at install.
|
|
14865
|
+
*
|
|
14866
|
+
* It is not `state.write` in disguise and does not imply it: a pass builds actions, it does not
|
|
14867
|
+
* run them, and the one piece of runtime state it can touch is a scene-local flag the compiler
|
|
14868
|
+
* creates and owns (`SceneCompileContext.runtimeFlag`), never a story variable the author
|
|
14869
|
+
* declared.
|
|
14870
|
+
*/
|
|
14871
|
+
readonly StoryCompile: "story.compile";
|
|
14872
|
+
/**
|
|
14873
|
+
* `app.game.diagnostics` — read what the player's caches are holding.
|
|
14874
|
+
*
|
|
14875
|
+
* The lightest of the list to grant and the easiest to misread as heavier than it is. Nothing
|
|
14876
|
+
* here is authored data: it reports how many images the engine is holding, how many bytes those
|
|
14877
|
+
* cost, and what budgets they are being held against. It reads no story, no save and no
|
|
14878
|
+
* variable, and every member is a read - there is nothing to set.
|
|
14879
|
+
*
|
|
14880
|
+
* It is its own capability rather than free like `data` and `config` because a plugin that
|
|
14881
|
+
* watches memory is a plugin that runs continuously and reports numbers about the player's
|
|
14882
|
+
* machine, and an author should see that named at install. The performance inspector is the
|
|
14883
|
+
* plugin this exists for.
|
|
14884
|
+
*
|
|
14885
|
+
* Absent until a game session is live, which is the same rule every other engine-backed member
|
|
14886
|
+
* follows: a plugin's `setup()` runs during boot, and asking then answers null rather than
|
|
14887
|
+
* throwing.
|
|
14888
|
+
*/
|
|
14889
|
+
readonly Diagnostics: "diagnostics";
|
|
14890
|
+
/**
|
|
14891
|
+
* `app.game.process.memory()` — read how much memory the game's processes hold, as the
|
|
14892
|
+
* operating system counts it.
|
|
14893
|
+
*
|
|
14894
|
+
* Its own capability rather than a member of `diagnostics`, because what it reports is not the
|
|
14895
|
+
* game's: `diagnostics` describes the engine's caches, which are the game's own bookkeeping,
|
|
14896
|
+
* while this is the player's machine - how much of its memory every process of the game is
|
|
14897
|
+
* holding, the main process and the GPU process included. An author who approved the first did
|
|
14898
|
+
* not approve the second, and a plugin that already holds `diagnostics` must not gain this by
|
|
14899
|
+
* an update without being asked again - which is what a capability of its own guarantees.
|
|
14900
|
+
*
|
|
14901
|
+
* Read-only, and numbers only: sizes and what each process is for, never a path, an id or a
|
|
14902
|
+
* command line. Nothing is sent anywhere by the host; what a plugin does with a reading is
|
|
14903
|
+
* visible in the code the author installed.
|
|
14904
|
+
*
|
|
14905
|
+
* Absent where there are no processes to count - the web export - and narrowed in Dev Mode to
|
|
14906
|
+
* the window's own renderer, since everything around it is Studio's.
|
|
14907
|
+
*/
|
|
14908
|
+
readonly ProcessMemory: "process.memory";
|
|
14909
|
+
};
|
|
14910
|
+
type PluginRuntimeCapability = typeof PluginRuntimeCapability[keyof typeof PluginRuntimeCapability];
|
|
14911
|
+
/**
|
|
14912
|
+
* `app.game.sidecar` has no entry here on purpose: it exists exactly when
|
|
14913
|
+
* `contributes.sidecars` is non-empty. Declaring the sidecar *is* the request.
|
|
14914
|
+
*/
|
|
14915
|
+
export interface PluginIdentity {
|
|
14916
|
+
id: string;
|
|
14917
|
+
name?: string;
|
|
14918
|
+
version?: string;
|
|
14919
|
+
publisher?: string;
|
|
14920
|
+
}
|
|
14921
|
+
interface PluginPermissionRequestBase {
|
|
14922
|
+
requestId: string;
|
|
14923
|
+
plugin: PluginIdentity;
|
|
14924
|
+
reason?: string;
|
|
14925
|
+
requestedAt?: number;
|
|
14926
|
+
}
|
|
14927
|
+
export type PluginPermissionRequest = (PluginPermissionRequestBase & {
|
|
14928
|
+
kind: "trust";
|
|
14929
|
+
persistence?: PluginPermissionPersistence;
|
|
14930
|
+
}) | (PluginPermissionRequestBase & {
|
|
14931
|
+
kind: "filesystem";
|
|
14932
|
+
path: string;
|
|
14933
|
+
mode: PluginFileSystemPermissionMode;
|
|
14934
|
+
recursive: boolean;
|
|
14935
|
+
persistence: PluginPermissionPersistence;
|
|
14936
|
+
}) | (PluginPermissionRequestBase & {
|
|
14937
|
+
kind: "install";
|
|
14938
|
+
source: string;
|
|
14939
|
+
permissions?: PluginInstallPermission[];
|
|
14940
|
+
persistence?: PluginPermissionPersistence;
|
|
14941
|
+
}) | (PluginPermissionRequestBase & {
|
|
14942
|
+
kind: "api";
|
|
14943
|
+
capability: string;
|
|
14944
|
+
persistence?: PluginPermissionPersistence;
|
|
14945
|
+
});
|
|
14946
|
+
interface PluginPermissionGrantResult {
|
|
14947
|
+
requestId: string;
|
|
14948
|
+
pluginId: string;
|
|
14949
|
+
kind: PluginPermissionRequest["kind"];
|
|
14950
|
+
approved: boolean;
|
|
14951
|
+
persistence: PluginPermissionPersistence;
|
|
14952
|
+
grantedAt?: number;
|
|
14953
|
+
}
|
|
14954
|
+
export type PluginPermissionPromptResult = PluginPermissionGrantResult | null;
|
|
14955
|
+
declare const PluginManifestVersion = 2;
|
|
14956
|
+
/**
|
|
14957
|
+
* Per-target plugin entry files. Each entry is a prebundled ESM file relative
|
|
14958
|
+
* to the plugin package root. At least one target must be declared.
|
|
14959
|
+
*
|
|
14960
|
+
* - `studio`: loaded in the workspace window only; talks to the
|
|
14961
|
+
* `narraleaf-studio/plugin` host API (editor extensions).
|
|
14962
|
+
* - `runtime`: loaded in every game execution environment (Dev Mode window,
|
|
14963
|
+
* Preview, Production); talks to the `narraleaf-studio/runtime` host API
|
|
14964
|
+
* (game logic such as blueprint node execute bindings).
|
|
14965
|
+
*/
|
|
14966
|
+
export type PluginManifestEntries = {
|
|
14967
|
+
studio?: string;
|
|
14968
|
+
runtime?: string;
|
|
14969
|
+
};
|
|
14970
|
+
type PluginLocaleContribution = {
|
|
14971
|
+
code: string;
|
|
14972
|
+
nativeName?: string;
|
|
14973
|
+
englishName?: string;
|
|
14974
|
+
intl?: string;
|
|
14975
|
+
dir?: "ltr" | "rtl";
|
|
14976
|
+
messages: string;
|
|
14977
|
+
};
|
|
14978
|
+
type PluginBinaryPlatformKey = string;
|
|
14979
|
+
type PluginSidecarTargetContribution = {
|
|
14980
|
+
/** Executable (or, for `kind: "node"`, the .js file) to run. Must appear in `include`. */
|
|
14981
|
+
entry: string;
|
|
14982
|
+
/**
|
|
14983
|
+
* Everything shipped for this platform. Entries are package-relative paths,
|
|
14984
|
+
* or `dep:<buildDependencyId>/<path>` to pull an artifact produced by a
|
|
14985
|
+
* declared build dependency (that is how third-party redistributables that
|
|
14986
|
+
* we may not vendor ourselves reach the pack).
|
|
14987
|
+
*/
|
|
14988
|
+
include: string[];
|
|
14989
|
+
/**
|
|
14990
|
+
* `sha256` of every package-relative entry in `include`, hex. Verified at
|
|
14991
|
+
* install and again at pack time, so a tampered package fails to install
|
|
14992
|
+
* instead of silently shipping a different binary. `dep:` entries are covered
|
|
14993
|
+
* by the build dependency's own digest instead.
|
|
14994
|
+
*/
|
|
14995
|
+
sha256: Record<string, string>;
|
|
14996
|
+
};
|
|
14997
|
+
type PluginSidecarContribution = {
|
|
14998
|
+
/** Prefixed with the plugin id, like every other contributed identifier. */
|
|
14999
|
+
id: string;
|
|
15000
|
+
/**
|
|
15001
|
+
* `executable` spawns the binary directly and talks to it over stdin/stdout; `node` runs the
|
|
15002
|
+
* .js as an Electron utility process and talks to it over `process.parentPort`, which is the
|
|
15003
|
+
* one channel a utility process has - it has no stdin.
|
|
15004
|
+
*/
|
|
15005
|
+
kind: PluginSidecarKind;
|
|
15006
|
+
/**
|
|
15007
|
+
* Newline-delimited JSON, which is the framing and not the channel: where the frames travel is
|
|
15008
|
+
* {@link kind}'s business, and only an executable sidecar's travel over stdio. Either way
|
|
15009
|
+
* stderr stays a plain log channel.
|
|
15010
|
+
*
|
|
15011
|
+
* `stdio-jsonl` is the older spelling of the same thing, from when there was only one channel
|
|
15012
|
+
* it could mean. Manifests that say it keep working and always will - a published plugin is a
|
|
15013
|
+
* file somebody already shipped - but a new one should say `jsonl`.
|
|
15014
|
+
*/
|
|
15015
|
+
transport: "jsonl" | "stdio-jsonl";
|
|
15016
|
+
/** `onGameStart` spawns with the window; `onRequest` waits for the first call. */
|
|
15017
|
+
autostart: "onGameStart" | "onRequest";
|
|
15018
|
+
/** How long the handshake may take before the sidecar counts as unavailable. */
|
|
15019
|
+
startupTimeoutMs: number;
|
|
15020
|
+
/** Grace period between the shutdown message and SIGTERM. */
|
|
15021
|
+
shutdownTimeoutMs: number;
|
|
15022
|
+
restart: {
|
|
15023
|
+
maxRetries: number;
|
|
15024
|
+
backoffMs: number;
|
|
15025
|
+
};
|
|
15026
|
+
targets: Record<PluginBinaryPlatformKey, PluginSidecarTargetContribution>;
|
|
15027
|
+
};
|
|
15028
|
+
type PluginBuildDependencyTargetContribution = {
|
|
15029
|
+
url: string;
|
|
15030
|
+
/** Mandatory. Doubles as the cache key, so re-pointing the URL at identical bytes never re-downloads. */
|
|
15031
|
+
sha256: string;
|
|
15032
|
+
archive: "zip";
|
|
15033
|
+
/** Archive-internal path -> path inside the produced dependency directory. */
|
|
15034
|
+
files: Record<string, string>;
|
|
15035
|
+
} | {
|
|
15036
|
+
url: string;
|
|
15037
|
+
sha256: string;
|
|
15038
|
+
archive: "none";
|
|
15039
|
+
/** Name the downloaded file takes inside the produced dependency directory. */
|
|
15040
|
+
fileName: string;
|
|
15041
|
+
};
|
|
15042
|
+
type PluginBuildDependencyContribution = {
|
|
15043
|
+
/** Prefixed with the plugin id. Referenced from sidecar `include` as `dep:<id>/<path>`. */
|
|
15044
|
+
id: string;
|
|
15045
|
+
/** Shown to the author at install and in build logs; say what the binaries are. */
|
|
15046
|
+
description?: string;
|
|
15047
|
+
targets: Record<PluginBinaryPlatformKey, PluginBuildDependencyTargetContribution>;
|
|
15048
|
+
};
|
|
15049
|
+
declare const PLUGIN_BUILD_CONFIG_TYPES: readonly [
|
|
15050
|
+
"text",
|
|
15051
|
+
"secret"
|
|
15052
|
+
];
|
|
15053
|
+
type PluginBuildConfigType = (typeof PLUGIN_BUILD_CONFIG_TYPES)[number];
|
|
15054
|
+
declare const PLUGIN_BUILD_CONFIG_SCOPES: readonly [
|
|
15055
|
+
"global",
|
|
15056
|
+
"variant",
|
|
15057
|
+
"platform",
|
|
15058
|
+
"variant-platform"
|
|
15059
|
+
];
|
|
15060
|
+
type PluginBuildConfigScope = (typeof PLUGIN_BUILD_CONFIG_SCOPES)[number];
|
|
15061
|
+
type PluginBuildConfigFieldContribution = {
|
|
15062
|
+
/** Unique within the plugin. Keys the stored value; never displayed. */
|
|
15063
|
+
key: string;
|
|
15064
|
+
/** Author-facing name of the field. */
|
|
15065
|
+
label: string;
|
|
15066
|
+
/** One line saying what the value is for. */
|
|
15067
|
+
description?: string;
|
|
15068
|
+
/** `text` is stored in the project; `secret` is not - the project stores a handle. */
|
|
15069
|
+
type: PluginBuildConfigType;
|
|
15070
|
+
/** Which builds share one value. */
|
|
15071
|
+
scope: PluginBuildConfigScope;
|
|
15072
|
+
/** Platforms the field applies to. Absent means every platform. */
|
|
15073
|
+
platforms?: GameBuildPlatform[];
|
|
15074
|
+
/** A build that would ship without a value for this field is refused. */
|
|
15075
|
+
required?: boolean;
|
|
15076
|
+
};
|
|
15077
|
+
export type PluginContributes = {
|
|
15078
|
+
/** Blueprint node types this plugin provides (editor def + runtime execute). */
|
|
15079
|
+
blueprintNodes?: string[];
|
|
15080
|
+
/** Widget element types this plugin provides (editor module + runtime renderer). */
|
|
15081
|
+
widgets?: string[];
|
|
15082
|
+
/** Studio language packs: locales this plugin adds or fills. */
|
|
15083
|
+
locales?: PluginLocaleContribution[];
|
|
15084
|
+
/**
|
|
15085
|
+
* Plugin storage namespaces to publish with the game, readable at runtime
|
|
15086
|
+
* through `app.game.data.readJson(namespace)`.
|
|
15087
|
+
*
|
|
15088
|
+
* Plugin stores live under the project's `editor/` directory, which is never
|
|
15089
|
+
* packaged. A plugin whose runtime needs authored data (catalogs, tables)
|
|
15090
|
+
* must list those namespaces here. The list is an explicit allowlist so
|
|
15091
|
+
* editor-only plugin state cannot leak into a shipped game by accident.
|
|
15092
|
+
*/
|
|
15093
|
+
runtimeData?: string[];
|
|
15094
|
+
/**
|
|
15095
|
+
* Test ids this plugin registers with `app.services.tests` (see
|
|
15096
|
+
* `docs/plugin-test-protocol.md`). Declaring them lets the Launcher say what
|
|
15097
|
+
* a plugin checks before any of its code runs, and registering an
|
|
15098
|
+
* undeclared id throws at load.
|
|
15099
|
+
*
|
|
15100
|
+
* Unlike every other code-backed contribution here, this one derives no
|
|
15101
|
+
* install permission — a test only ever executes because the author picked
|
|
15102
|
+
* it out of the Run > Test dialog and pressed Start, so there is no ambient
|
|
15103
|
+
* capability to consent to at install time.
|
|
15104
|
+
*/
|
|
15105
|
+
tests?: string[];
|
|
15106
|
+
/**
|
|
15107
|
+
* Save ids the plugin keeps for its own bookkeeping - a quick-save slot, a checkpoint - rather
|
|
15108
|
+
* than slots a player chose.
|
|
15109
|
+
*
|
|
15110
|
+
* The game leaves them out of `List Saves`, and so out of every save screen built on it, the way
|
|
15111
|
+
* it leaves out its own autosave ring. Nothing else changes for them: the plugin still writes,
|
|
15112
|
+
* loads and finds them through `app.game.saves` (whose `listIds` is the raw store listing and
|
|
15113
|
+
* still includes them), and the explicit-id save nodes - `Load Save`, `Delete Save`, the
|
|
15114
|
+
* `Get Save` readers - take them like any other id.
|
|
15115
|
+
*
|
|
15116
|
+
* Prefixed with the plugin id like the type lists above, so a plugin can only reserve ids in its
|
|
15117
|
+
* own namespace and can never hide a slot the author's own `Save Game` wrote. That is also why it
|
|
15118
|
+
* derives no install permission: it reaches nothing the plugin did not already own.
|
|
15119
|
+
*/
|
|
15120
|
+
reservedSaveIds?: string[];
|
|
15121
|
+
/**
|
|
15122
|
+
* Capability domains the `runtime` entry may use. Each maps 1:1 onto a
|
|
15123
|
+
* namespace on `app.game`, and an undeclared domain is absent from that
|
|
15124
|
+
* object rather than present-and-throwing — so what the install prompt
|
|
15125
|
+
* listed and what the plugin can reach are the same set by construction.
|
|
15126
|
+
*/
|
|
15127
|
+
runtimeCapabilities?: PluginRuntimeCapability[];
|
|
15128
|
+
/** Native child processes shipped inside the author's game. */
|
|
15129
|
+
sidecars?: PluginSidecarContribution[];
|
|
15130
|
+
/** External binaries fetched (and cached) at build time. */
|
|
15131
|
+
buildDependencies?: PluginBuildDependencyContribution[];
|
|
15132
|
+
/**
|
|
15133
|
+
* Values the author supplies before a build can ship. Declaration only - see
|
|
15134
|
+
* {@link PluginBuildConfigFieldContribution} for why there is no registration API and why this
|
|
15135
|
+
* grants the plugin nothing.
|
|
15136
|
+
*/
|
|
15137
|
+
buildConfig?: PluginBuildConfigFieldContribution[];
|
|
15138
|
+
/**
|
|
15139
|
+
* Address patterns this plugin may hand to the player's browser or platform handler, through
|
|
15140
|
+
* `app.game.navigation.openExternal`.
|
|
15141
|
+
*
|
|
15142
|
+
* Unlike everything else here, this one is *not* a list of identifiers the plugin owns - it is a
|
|
15143
|
+
* list of places outside the game it may reach, so it derives an install permission the author
|
|
15144
|
+
* approves by name (see `PluginInstallPermission` `kind: "externalLink"`) and adding one to a
|
|
15145
|
+
* later version re-prompts.
|
|
15146
|
+
*
|
|
15147
|
+
* Patterns are matched structurally, never as strings: see `isExternalLinkPatternDeclared` in
|
|
15148
|
+
* `@shared/types/externalLinkPattern` for the exact semantics. Wildcards are allowed
|
|
15149
|
+
* (`https://*.example.com/app/*`) and the scheme is not restricted to `http(s)`, because the
|
|
15150
|
+
* storefronts a plugin integrates with speak their own (`steam://run/480`). A handful of schemes
|
|
15151
|
+
* are refused at validation whatever a manifest says; that list and its reasoning live in the
|
|
15152
|
+
* same module.
|
|
15153
|
+
*
|
|
15154
|
+
* This is separate from, and grants nothing towards, {@link network}: opening a page and
|
|
15155
|
+
* fetching bytes are different acts, and the Open Link node the author writes is a third thing
|
|
15156
|
+
* again, governed only by its scheme.
|
|
15157
|
+
*/
|
|
15158
|
+
externalLinks?: string[];
|
|
15159
|
+
/**
|
|
15160
|
+
* Address patterns this plugin's runtime code requests bytes from.
|
|
15161
|
+
*
|
|
15162
|
+
* Like {@link externalLinks} and unlike everything else here, this is a list of places outside
|
|
15163
|
+
* the game rather than identifiers the plugin owns, so it derives an install permission the
|
|
15164
|
+
* author approves by name (`PluginInstallPermission` `kind: "network"`) and adding one to a
|
|
15165
|
+
* later version re-prompts.
|
|
15166
|
+
*
|
|
15167
|
+
* Two rules narrower than the external-link patterns, both for the same reason - what comes
|
|
15168
|
+
* back from a fetch runs inside the game, while an opened page does not:
|
|
15169
|
+
*
|
|
15170
|
+
* - **`http(s)` only.** There is no storefront-scheme case here; a plugin that fetches speaks
|
|
15171
|
+
* HTTP.
|
|
15172
|
+
* - **The path is written out.** `https://api.example.com/*` is a declaration and
|
|
15173
|
+
* `https://api.example.com` is not, because the pattern language reads the second as the
|
|
15174
|
+
* single path `/`. It is refused rather than rewritten: this string is what the install
|
|
15175
|
+
* prompt shows, and a manifest whose text differs from what was approved is the one thing
|
|
15176
|
+
* this whole mechanism exists to prevent.
|
|
15177
|
+
*
|
|
15178
|
+
* What this reaches is *not* widened by the project's own network allowlist and does not widen
|
|
15179
|
+
* it. A build narrowed to a list still reaches these hosts - the author approved them at
|
|
15180
|
+
* install - and the list shows them attributed to this plugin rather than quietly making room.
|
|
15181
|
+
*/
|
|
15182
|
+
network?: string[];
|
|
15183
|
+
/**
|
|
15184
|
+
* The props of a contributed widget that hold words a player reads, by widget type.
|
|
15185
|
+
*
|
|
15186
|
+
* Each type named here must also be in {@link widgets}. A declared prop is treated as Studio's own
|
|
15187
|
+
* text and button labels are: the properties panel offers its words written directly or read
|
|
15188
|
+
* from a translation key, the canvas draws a key's words, the words go into the project's
|
|
15189
|
+
* translation table and exports, the project checks report a missing translation or key, and
|
|
15190
|
+
* the glyph check reads them. In the game the widget's renderer is handed the words already in
|
|
15191
|
+
* the player's language, in the prop it declared - it never resolves a key itself.
|
|
15192
|
+
*
|
|
15193
|
+
* Declared in the manifest rather than at registration because both entries need it - the
|
|
15194
|
+
* studio entry's widget is edited by it and the runtime entry's is drawn by it - and a
|
|
15195
|
+
* declaration only one of them made would be the half that drifts.
|
|
15196
|
+
*/
|
|
15197
|
+
widgetText?: Record<string, PluginWidgetTextContribution[]>;
|
|
15198
|
+
/**
|
|
15199
|
+
* The shapes of the rows this plugin's nodes hand out, so a list can be given one and a field
|
|
15200
|
+
* read from one by name.
|
|
15201
|
+
*
|
|
15202
|
+
* A node pin carrying them says `struct:<id>` or `array<struct:<id>>` - `Get Gallery` answers
|
|
15203
|
+
* `array<struct:narraleaf.gallery.entry>` - and from then on the plugin's rows are what the
|
|
15204
|
+
* engine's own are: a list picks the shape under Edit content and its fields are the plugin's
|
|
15205
|
+
* and locked, Get Field offers them, and a list handed rows that lack them is reported.
|
|
15206
|
+
*
|
|
15207
|
+
* Declared in the manifest rather than at registration because the editor, the running game and
|
|
15208
|
+
* the command-line checker all need the shape, and only the manifest is read by all three before
|
|
15209
|
+
* any plugin code runs. Ids must start with the plugin id.
|
|
15210
|
+
*/
|
|
15211
|
+
structs?: PluginStructContribution[];
|
|
15212
|
+
};
|
|
15213
|
+
type PluginStructContribution = {
|
|
15214
|
+
/** Prefixed with the plugin id: `narraleaf.gallery.entry`. */
|
|
15215
|
+
id: string;
|
|
15216
|
+
/** What the editor calls the shape - "Gallery entry". */
|
|
15217
|
+
name: string;
|
|
15218
|
+
/** `name` per editor locale, matched exactly as `localized` names are. */
|
|
15219
|
+
localized?: Record<string, string>;
|
|
15220
|
+
/** In the order the rows are read: the list's content editor shows them in this order. */
|
|
15221
|
+
fields: {
|
|
15222
|
+
key: string;
|
|
15223
|
+
type: UIStructFieldType;
|
|
15224
|
+
}[];
|
|
15225
|
+
};
|
|
15226
|
+
/**
|
|
15227
|
+
* One prop of a contributed widget whose value is words a player reads.
|
|
15228
|
+
*
|
|
15229
|
+
* `keyProp` is the prop the widget's translation key is kept in when the author reads the words from
|
|
15230
|
+
* one. It defaults to `<prop>LocalizationKey`, and it is the host's to write: the widget never reads it
|
|
15231
|
+
* to draw, because the words in `prop` already come from the key when there is one.
|
|
15232
|
+
*/
|
|
15233
|
+
export type PluginWidgetTextContribution = {
|
|
15234
|
+
/** The prop holding the words. */
|
|
15235
|
+
prop: string;
|
|
15236
|
+
/** The prop the translation key is kept in; `<prop>LocalizationKey` when not declared. */
|
|
15237
|
+
keyProp?: string;
|
|
15238
|
+
/** What the properties panel calls the words. The prop name when not declared. */
|
|
15239
|
+
label?: string;
|
|
15240
|
+
/** `label` per editor locale, matched exactly as `localized` names are. */
|
|
15241
|
+
localized?: Record<string, string>;
|
|
15242
|
+
/** Whether the words may run over several lines; one line when not declared. */
|
|
15243
|
+
multiline?: boolean;
|
|
15244
|
+
};
|
|
15245
|
+
/**
|
|
15246
|
+
* The plugin's name and description in one editor language. Either may be left out, and the plain
|
|
15247
|
+
* manifest field is shown in its place.
|
|
15248
|
+
*/
|
|
15249
|
+
export type PluginManifestLocalizedText = {
|
|
15250
|
+
name?: string;
|
|
15251
|
+
description?: string;
|
|
15252
|
+
};
|
|
15253
|
+
/**
|
|
15254
|
+
* `name` and `description` per editor locale, keyed by the exact code Studio's language setting
|
|
15255
|
+
* stores (`zh`, `ja`, or a code a language pack adds). There is no region matching: an entry under
|
|
15256
|
+
* `zh-CN` is never shown, because no Studio locale is called that.
|
|
15257
|
+
*/
|
|
15258
|
+
export type PluginManifestLocalized = Record<string, PluginManifestLocalizedText>;
|
|
15259
|
+
export type PluginManifestV2 = Omit<PluginIdentity, "id" | "name" | "version"> & Required<Pick<PluginIdentity, "id" | "name" | "version">> & {
|
|
15260
|
+
manifestVersion: typeof PluginManifestVersion;
|
|
15261
|
+
description?: string;
|
|
15262
|
+
/**
|
|
15263
|
+
* Translations of `name` and `description`, shown wherever Studio names the plugin to an author
|
|
15264
|
+
* whose editor is set to that language. The plain fields stay the plugin's identity: command
|
|
15265
|
+
* lines, project files and built games name it by them.
|
|
15266
|
+
*/
|
|
15267
|
+
localized?: PluginManifestLocalized;
|
|
15268
|
+
/**
|
|
15269
|
+
* Package-relative path to the thumbnail shown beside the plugin's name in
|
|
15270
|
+
* the Launcher list. Square, at most 512x512, and one of the extensions in
|
|
15271
|
+
* `PLUGIN_ICON_EXTENSIONS`. Plugins without one keep the name monogram.
|
|
15272
|
+
*/
|
|
15273
|
+
icon?: string;
|
|
15274
|
+
entries: PluginManifestEntries;
|
|
15275
|
+
contributes?: PluginContributes;
|
|
15276
|
+
permissions?: PluginInstallPermission[];
|
|
15277
|
+
};
|
|
15278
|
+
export type NormalizedPluginManifestV2 = PluginManifestV2 & {
|
|
15279
|
+
contributes: Required<PluginContributes>;
|
|
15280
|
+
permissions: PluginInstallPermission[];
|
|
15281
|
+
};
|
|
15282
|
+
type PluginInstallSource = {
|
|
15283
|
+
kind: "local-directory";
|
|
15284
|
+
path: string;
|
|
15285
|
+
} | {
|
|
15286
|
+
kind: "builtin";
|
|
15287
|
+
path: string;
|
|
15288
|
+
} | {
|
|
15289
|
+
kind: "registry";
|
|
15290
|
+
url: string;
|
|
15291
|
+
};
|
|
15292
|
+
export type PluginInstallRecord = {
|
|
15293
|
+
pluginId: string;
|
|
15294
|
+
installPath: string;
|
|
15295
|
+
enabled: boolean;
|
|
15296
|
+
builtIn: boolean;
|
|
15297
|
+
manifest: NormalizedPluginManifestV2;
|
|
15298
|
+
installSource: PluginInstallSource;
|
|
15299
|
+
installedAt: number;
|
|
15300
|
+
updatedAt: number;
|
|
15301
|
+
grantedManifestVersion?: string | null;
|
|
15302
|
+
/**
|
|
15303
|
+
* The permission set the user actually approved, recorded so a later version
|
|
15304
|
+
* that does not widen it can inherit the grant instead of re-prompting.
|
|
15305
|
+
* Absent on records written before this was tracked — the manifest at
|
|
15306
|
+
* `grantedManifestVersion` is the fallback source of truth for those.
|
|
15307
|
+
*/
|
|
15308
|
+
grantedPermissions?: PluginInstallPermission[] | null;
|
|
15309
|
+
lastError?: string | null;
|
|
15310
|
+
};
|
|
15311
|
+
type PluginStatus = "enabled" | "disabled" | "needsAuthorization" | "error";
|
|
15312
|
+
export type PluginListItem = {
|
|
15313
|
+
pluginId: string;
|
|
15314
|
+
manifest: NormalizedPluginManifestV2;
|
|
15315
|
+
/**
|
|
15316
|
+
* `app://` address of the declared icon, absent when the plugin ships none.
|
|
15317
|
+
* Resolved here rather than in the renderer because only the main process
|
|
15318
|
+
* knows where the package landed on disk.
|
|
15319
|
+
*/
|
|
15320
|
+
iconUrl?: string;
|
|
15321
|
+
installPath: string;
|
|
15322
|
+
enabled: boolean;
|
|
15323
|
+
builtIn: boolean;
|
|
15324
|
+
status: PluginStatus;
|
|
15325
|
+
installSource: PluginInstallSource;
|
|
15326
|
+
installedAt: number;
|
|
15327
|
+
updatedAt: number;
|
|
15328
|
+
grantedManifestVersion?: string | null;
|
|
15329
|
+
lastError?: string | null;
|
|
15330
|
+
};
|
|
15331
|
+
/** Descriptor handed to the workspace window: plugins with a studio entry. */
|
|
15332
|
+
export type WorkspacePluginDescriptor = {
|
|
15333
|
+
plugin: PluginIdentity;
|
|
15334
|
+
manifest: NormalizedPluginManifestV2;
|
|
15335
|
+
entryUrl: string;
|
|
15336
|
+
};
|
|
15337
|
+
/** Descriptor handed to game execution environments: plugins with a runtime entry. */
|
|
15338
|
+
export type RuntimePluginDescriptor = {
|
|
15339
|
+
plugin: PluginIdentity;
|
|
15340
|
+
manifest: NormalizedPluginManifestV2;
|
|
15341
|
+
entryUrl: string;
|
|
15342
|
+
/**
|
|
15343
|
+
* Published plugin storage, keyed by the namespaces declared in
|
|
15344
|
+
* `contributes.runtimeData`. Absent namespaces simply have no entry - a
|
|
15345
|
+
* plugin must tolerate missing data (the project may never have written it).
|
|
15346
|
+
*/
|
|
15347
|
+
data?: Record<string, unknown>;
|
|
15348
|
+
/**
|
|
15349
|
+
* This plugin's own `contributes.buildConfig` values, as the build that produced the pack
|
|
15350
|
+
* resolved them, keyed by field key. Only ever the declaring plugin's own values: the pack
|
|
15351
|
+
* holds one record per plugin and a descriptor is built from one entry.
|
|
15352
|
+
*
|
|
15353
|
+
* Absent outside a compiled pack - the editor and Dev Mode hand out descriptors that were never
|
|
15354
|
+
* built for a variant, so there is nothing resolved to carry.
|
|
15355
|
+
*/
|
|
15356
|
+
buildConfig?: Record<string, string>;
|
|
15357
|
+
};
|
|
15358
|
+
type GameMenuLabel = {
|
|
15359
|
+
readonly key?: string | null;
|
|
15360
|
+
readonly text: string;
|
|
15361
|
+
readonly words?: string | null;
|
|
15362
|
+
};
|
|
15363
|
+
declare const GAME_MENU_DYNAMIC_SOURCES: readonly [
|
|
15364
|
+
"textLanguage",
|
|
15365
|
+
"voiceLanguage",
|
|
15366
|
+
"windowScale"
|
|
15367
|
+
];
|
|
15368
|
+
type GameMenuDynamicSource = typeof GAME_MENU_DYNAMIC_SOURCES[number];
|
|
15369
|
+
type GameMenuAction =
|
|
15370
|
+
/** Open an app surface as a page. */
|
|
15371
|
+
{
|
|
15372
|
+
readonly type: "openPage";
|
|
15373
|
+
readonly surfaceId: string;
|
|
15374
|
+
}
|
|
15375
|
+
/** Open an app surface as a layer over whatever is on screen. */
|
|
15376
|
+
| {
|
|
15377
|
+
readonly type: "openLayer";
|
|
15378
|
+
readonly surfaceId: string;
|
|
15379
|
+
readonly modal?: boolean;
|
|
15380
|
+
readonly dismissible?: boolean;
|
|
15381
|
+
readonly group?: string | null;
|
|
15382
|
+
}
|
|
15383
|
+
/** End the playthrough and hand the screen to a page - the same act as the story's quit row. */
|
|
15384
|
+
| {
|
|
15385
|
+
readonly type: "quitToPage";
|
|
15386
|
+
readonly surfaceId: string;
|
|
15387
|
+
}
|
|
15388
|
+
/**
|
|
15389
|
+
* Close the game.
|
|
15390
|
+
*
|
|
15391
|
+
* The same act as the Quit Application node, which means the same terms: a graceful quit that
|
|
15392
|
+
* does NOT run the author's close handler (`On Window Close Requested` answers the window
|
|
15393
|
+
* frame's own close, and the node deliberately stands the guard aside so a quit the game asked
|
|
15394
|
+
* for is not asked back about). An author who wants their own "really quit?" screen binds a
|
|
15395
|
+
* function instead and calls the node at the end of it - which is the same answer this menu
|
|
15396
|
+
* gives for every other confirmation.
|
|
15397
|
+
*/
|
|
15398
|
+
| {
|
|
15399
|
+
readonly type: "quitApp";
|
|
15400
|
+
}
|
|
15401
|
+
/** Advance the line on screen. */
|
|
15402
|
+
| {
|
|
15403
|
+
readonly type: "next";
|
|
15404
|
+
}
|
|
15405
|
+
/** Auto-forward on/off. Ticked while on. */
|
|
15406
|
+
| {
|
|
15407
|
+
readonly type: "toggleAutoForward";
|
|
15408
|
+
}
|
|
15409
|
+
/** Skipping on/off. Ticked while running. */
|
|
15410
|
+
| {
|
|
15411
|
+
readonly type: "toggleSkipping";
|
|
15412
|
+
}
|
|
15413
|
+
/** Which lines skipping stops at. The two values are a radio pair, never one switch. */
|
|
15414
|
+
| {
|
|
15415
|
+
readonly type: "setSkipReadText";
|
|
15416
|
+
readonly value: boolean;
|
|
15417
|
+
}
|
|
15418
|
+
/** Show or hide the dialogue box. Ticked while shown. */
|
|
15419
|
+
| {
|
|
15420
|
+
readonly type: "toggleDialog";
|
|
15421
|
+
}
|
|
15422
|
+
/** Step back one line of history. Greyed out when there is none. */
|
|
15423
|
+
| {
|
|
15424
|
+
readonly type: "historyUndo";
|
|
15425
|
+
}
|
|
15426
|
+
/** Step forward again after an undo. Greyed out when there is none. */
|
|
15427
|
+
| {
|
|
15428
|
+
readonly type: "historyRedo";
|
|
15429
|
+
}
|
|
15430
|
+
/** Full-screen on/off. Ticked while full-screen, including when the OS did it. */
|
|
15431
|
+
| {
|
|
15432
|
+
readonly type: "toggleFullscreen";
|
|
15433
|
+
}
|
|
15434
|
+
/**
|
|
15435
|
+
* Call a function declared in the global blueprint.
|
|
15436
|
+
*
|
|
15437
|
+
* `fnRef` is the catalogue's own reference (see `fnCatalog`), and `args` are keyed by the head's
|
|
15438
|
+
* parameter pin ids. Everything the named actions above do not cover goes through here -
|
|
15439
|
+
* including every confirmation, because what a game asks before losing a playthrough is the
|
|
15440
|
+
* author's screen and not a native box in a language the game may not even be running in.
|
|
15441
|
+
*/
|
|
15442
|
+
| {
|
|
15443
|
+
readonly type: "fn";
|
|
15444
|
+
readonly fnRef: string;
|
|
15445
|
+
readonly args?: Readonly<Record<string, unknown>>;
|
|
15446
|
+
};
|
|
15447
|
+
type GameMenuItemSpec = {
|
|
15448
|
+
readonly kind: "separator";
|
|
15449
|
+
} | {
|
|
15450
|
+
readonly kind: "submenu";
|
|
15451
|
+
readonly label: GameMenuLabel;
|
|
15452
|
+
readonly items: readonly GameMenuItemSpec[];
|
|
15453
|
+
} | {
|
|
15454
|
+
readonly kind: "action";
|
|
15455
|
+
readonly label: GameMenuLabel;
|
|
15456
|
+
readonly action: GameMenuAction;
|
|
15457
|
+
} | {
|
|
15458
|
+
readonly kind: "dynamic";
|
|
15459
|
+
readonly source: GameMenuDynamicSource;
|
|
15460
|
+
};
|
|
15461
|
+
type GameMenuSpecMenu = {
|
|
15462
|
+
readonly label: GameMenuLabel;
|
|
15463
|
+
readonly items: readonly GameMenuItemSpec[];
|
|
15464
|
+
};
|
|
15465
|
+
type GameMenuSpec = {
|
|
15466
|
+
readonly menus: readonly GameMenuSpecMenu[];
|
|
15467
|
+
};
|
|
15468
|
+
/**
|
|
15469
|
+
* How much memory a running game costs the machine it runs on, process by process.
|
|
15470
|
+
*
|
|
15471
|
+
* Read in the main process from Electron's process metrics (`app.getAppMetrics()`), which is the
|
|
15472
|
+
* operating system's own account: the same working set a task manager shows. Nothing a page can
|
|
15473
|
+
* read gives this. `performance.memory` describes one JavaScript heap, and in Electron it is also
|
|
15474
|
+
* rounded to coarse buckets - the two arms of a measured comparison both read exactly 92.9 MB -
|
|
15475
|
+
* while the memory a visual novel actually spends is mostly outside any heap:
|
|
15476
|
+
* decoded pictures, textures in the GPU process, audio buffers, the main process serving and
|
|
15477
|
+
* decrypting assets. The whole game is several processes, and only the process that owns them can
|
|
15478
|
+
* see all of them.
|
|
15479
|
+
*
|
|
15480
|
+
* `process.memoryUsage()` in the main process was the other candidate and answers a narrower
|
|
15481
|
+
* question - the main process's own heap and buffers - which is the one process a game's pictures
|
|
15482
|
+
* are not in.
|
|
15483
|
+
*
|
|
15484
|
+
* Comments in English per project convention.
|
|
15485
|
+
*/
|
|
15486
|
+
/** What a process is for, in the words a reader of a memory report needs. */
|
|
15487
|
+
type GameProcessKind = "main" | "renderer" | "gpu" | "utility" | "other";
|
|
15488
|
+
type GameProcessMemoryEntry = {
|
|
15489
|
+
kind: GameProcessKind;
|
|
15490
|
+
/**
|
|
15491
|
+
* What the platform calls it, when it says: a utility process names its service ("Network
|
|
15492
|
+
* Service", "Audio Service", a sidecar's own name). Absent for the rest.
|
|
15493
|
+
*/
|
|
15494
|
+
name?: string;
|
|
15495
|
+
/** Whether this is the process the asking page runs in. */
|
|
15496
|
+
current: boolean;
|
|
15497
|
+
/** Memory currently resident in physical RAM, in bytes. */
|
|
15498
|
+
workingSetBytes: number;
|
|
15499
|
+
/** The most this process has ever had resident, in bytes. */
|
|
15500
|
+
peakWorkingSetBytes: number;
|
|
15501
|
+
/** Memory no other process shares, in bytes. Windows reports it; other platforms do not. */
|
|
15502
|
+
privateBytes?: number;
|
|
15503
|
+
};
|
|
15504
|
+
type GameProcessMemoryReading = {
|
|
15505
|
+
/**
|
|
15506
|
+
* What the processes below add up to.
|
|
15507
|
+
*
|
|
15508
|
+
* - `game` - every process the game is made of. A packaged game and a preview.
|
|
15509
|
+
* - `window` - only the process that draws this window. Dev Mode: the window is Studio's, and
|
|
15510
|
+
* the processes around it (Studio's main process, its GPU process, its other windows) are
|
|
15511
|
+
* shared with everything else Studio has open, so counting them would report Studio.
|
|
15512
|
+
*/
|
|
15513
|
+
scope: "game" | "window";
|
|
15514
|
+
processes: GameProcessMemoryEntry[];
|
|
15515
|
+
/** The sum of every process's working set, in bytes. */
|
|
15516
|
+
workingSetBytes: number;
|
|
15517
|
+
/** The sum of every process's private bytes, or null where the platform does not report them. */
|
|
15518
|
+
privateBytes: number | null;
|
|
15519
|
+
};
|
|
15520
|
+
declare const UI_INPUT_POINTER_GESTURES: readonly [
|
|
15521
|
+
"click",
|
|
15522
|
+
"doubleClick",
|
|
15523
|
+
"rightClick",
|
|
15524
|
+
"wheelUp",
|
|
15525
|
+
"wheelDown",
|
|
15526
|
+
"wheelLeft",
|
|
15527
|
+
"wheelRight",
|
|
15528
|
+
"longPress",
|
|
15529
|
+
"middleClick"
|
|
15530
|
+
];
|
|
15531
|
+
type UIInputPointerGesture = (typeof UI_INPUT_POINTER_GESTURES)[number];
|
|
15532
|
+
type UIInputBinding = {
|
|
15533
|
+
kind: "pointer";
|
|
15534
|
+
gesture: UIInputPointerGesture;
|
|
15535
|
+
} | {
|
|
15536
|
+
kind: "key";
|
|
15537
|
+
key: string;
|
|
15538
|
+
} | {
|
|
15539
|
+
kind: "gamepad";
|
|
15540
|
+
button: string;
|
|
15541
|
+
};
|
|
15542
|
+
type UIInputActionDef = {
|
|
15543
|
+
id: string;
|
|
15544
|
+
/** Author-facing name. */
|
|
15545
|
+
name: string;
|
|
15546
|
+
/** The bindings a surface gets unless it overrides them. */
|
|
15547
|
+
bindings: UIInputBinding[];
|
|
15548
|
+
};
|
|
15549
|
+
type UISurfaceActionEnablement = {
|
|
15550
|
+
actionId: string;
|
|
15551
|
+
/** Whether firing this action stops the input here. Default true. */
|
|
15552
|
+
consume?: boolean;
|
|
15553
|
+
};
|
|
15554
|
+
declare const UI_PAGE_ANIMATION_PRESETS: readonly [
|
|
15555
|
+
"none",
|
|
15556
|
+
"fade",
|
|
15557
|
+
"slide",
|
|
15558
|
+
"push",
|
|
15559
|
+
"zoom",
|
|
15560
|
+
"pop",
|
|
15561
|
+
"blur"
|
|
15562
|
+
];
|
|
15563
|
+
declare const UI_PAGE_ANIMATION_DIRECTIONS: readonly [
|
|
15564
|
+
"auto",
|
|
15565
|
+
"left",
|
|
15566
|
+
"right",
|
|
15567
|
+
"up",
|
|
15568
|
+
"down",
|
|
15569
|
+
"angle"
|
|
15570
|
+
];
|
|
15571
|
+
type UIPageAnimationPreset = (typeof UI_PAGE_ANIMATION_PRESETS)[number];
|
|
15572
|
+
type UIPageAnimationDirection = (typeof UI_PAGE_ANIMATION_DIRECTIONS)[number];
|
|
15573
|
+
type UIPageAnimationSettings = {
|
|
15574
|
+
enter: UIPageAnimationPreset;
|
|
15575
|
+
exit: UIPageAnimationPreset;
|
|
15576
|
+
enterDirection: UIPageAnimationDirection;
|
|
15577
|
+
exitDirection: UIPageAnimationDirection;
|
|
15578
|
+
enterAngleDegrees: number;
|
|
15579
|
+
exitAngleDegrees: number;
|
|
15580
|
+
enterDurationSeconds: number;
|
|
15581
|
+
exitDurationSeconds: number;
|
|
15582
|
+
/** Held before this host's own enter starts, counted from the moment its subtree begins entering. */
|
|
15583
|
+
enterDelaySeconds: number;
|
|
15584
|
+
/** Held before this host's own exit starts, counted from the moment its subtree begins exiting. */
|
|
15585
|
+
exitDelaySeconds: number;
|
|
15586
|
+
/** Added to each successive direct child, so a row of children arrives (and leaves) one after another. */
|
|
15587
|
+
childStaggerSeconds: number;
|
|
15588
|
+
/** Hold this host's own exit until every child has finished leaving. Chains through nested parents. */
|
|
15589
|
+
exitWaitsForChildren: boolean;
|
|
15590
|
+
/** Surfaces only: hold the incoming Page until this exit has finished. */
|
|
15591
|
+
exitBlocking: boolean;
|
|
15592
|
+
};
|
|
15593
|
+
type UISurfaceBackgroundFillMode = "cover" | "contain" | "stretch" | "tile";
|
|
15594
|
+
type UISurfaceBackgroundImage = {
|
|
15595
|
+
/** Image library asset id. Absent background = absent field, so this is never empty. */
|
|
15596
|
+
assetId: string;
|
|
15597
|
+
fillMode: UISurfaceBackgroundFillMode;
|
|
15598
|
+
};
|
|
15599
|
+
type UIDocumentVersion = number;
|
|
15600
|
+
type UIDocumentId = string;
|
|
15601
|
+
type UISurfaceId = string;
|
|
15602
|
+
type UIElementId = string;
|
|
15603
|
+
type UIDocument = {
|
|
15604
|
+
schemaVersion: UIDocumentVersion;
|
|
15605
|
+
id: UIDocumentId;
|
|
15606
|
+
name: string;
|
|
15607
|
+
surfaces: UISurface[];
|
|
15608
|
+
components?: UIComponentDefinition[];
|
|
15609
|
+
elements: Record<UIElementId, UIElement>;
|
|
15610
|
+
/**
|
|
15611
|
+
* The shapes this document's widgets describe their data with, keyed by id.
|
|
15612
|
+
*
|
|
15613
|
+
* No authoring surface manages this table: an author edits the fields on the widget that uses
|
|
15614
|
+
* them, and a widget stores only the id. It is a document-level table rather than a copy on each
|
|
15615
|
+
* widget so that a shape has one spelling for the whole project - which is what lets one list's
|
|
15616
|
+
* content feed another's, and what a Break node's pins are generated from. The rules that keep
|
|
15617
|
+
* an implicit library safe to edit are in `struct.ts`.
|
|
15618
|
+
*/
|
|
15619
|
+
structs?: Record<UIStructId, UIStructDef>;
|
|
15620
|
+
/**
|
|
15621
|
+
* What the gestures of this project mean, keyed by id.
|
|
15622
|
+
*
|
|
15623
|
+
* The vocabulary half of the input model: "advance", "skip", "open the menu" - named once for
|
|
15624
|
+
* the whole project, with the bindings a surface gets unless it says otherwise. A surface stores
|
|
15625
|
+
* only the id, in {@link UISurface.actions}, so renaming an action here renames it everywhere
|
|
15626
|
+
* and rebinding it rebinds every surface that took the default. The rules are in
|
|
15627
|
+
* `inputAction.ts`.
|
|
15628
|
+
*/
|
|
15629
|
+
actions?: Record<string, UIInputActionDef>;
|
|
15630
|
+
/**
|
|
15631
|
+
* The page the game starts on, by id.
|
|
15632
|
+
*
|
|
15633
|
+
* Absent means the page carrying `MAIN_APP_SURFACE_ID` - the id a new document gives its first
|
|
15634
|
+
* page, and the only entry a document had before this field existed - so a document whose author
|
|
15635
|
+
* never moved the entry stays exactly as short as it was. Read only through `resolveEntrySurface`
|
|
15636
|
+
* (`entrySurface.ts`), which also says what a pointer that has lost its page falls back to.
|
|
15637
|
+
*
|
|
15638
|
+
* A pointer rather than an id swap: a page's id is what its blueprints are filed under and what
|
|
15639
|
+
* every `Go Page` names, so making another page the entry must not change which id it has.
|
|
15640
|
+
*
|
|
15641
|
+
* Carries no schema bump, for the reason `actions` and `structs` did not: absence already means a
|
|
15642
|
+
* defined answer, so a document written before this field reads correctly without one.
|
|
15643
|
+
*/
|
|
15644
|
+
entrySurfaceId?: UISurfaceId;
|
|
15645
|
+
meta?: UIDocumentMeta;
|
|
15646
|
+
};
|
|
15647
|
+
type UIDocumentMeta = {
|
|
15648
|
+
createdAt?: string;
|
|
15649
|
+
updatedAt?: string;
|
|
15650
|
+
};
|
|
15651
|
+
type UIHost = "app" | "player";
|
|
15652
|
+
type UISurfaceKind = "appSurface" | "stageSurface";
|
|
15653
|
+
type UIStageSlotId = "onStage" | "dialog" | "notification" | "choice" | "nvl";
|
|
15654
|
+
type UIStageSurfaceMount = {
|
|
15655
|
+
kind: "slot";
|
|
15656
|
+
slotId: UIStageSlotId;
|
|
15657
|
+
};
|
|
15658
|
+
type UIAppSurface = {
|
|
15659
|
+
id: UISurfaceId;
|
|
15660
|
+
name: string;
|
|
15661
|
+
host: "app";
|
|
15662
|
+
kind: "appSurface";
|
|
15663
|
+
designSize: UISurfaceDesignSize;
|
|
15664
|
+
rootElementId: UIElementId;
|
|
15665
|
+
settings?: UISurfaceSettings;
|
|
15666
|
+
/**
|
|
15667
|
+
* Which of the project's actions this surface answers. An id here names an entry of
|
|
15668
|
+
* {@link UIDocument.actions}; absent means it answers none of them.
|
|
15669
|
+
*
|
|
15670
|
+
* Input that lands on this surface's content stops here whether or not an action answered it,
|
|
15671
|
+
* unless the action that answered says otherwise. There is no surface-wide setting for that: a
|
|
15672
|
+
* surface drawn over another is drawn over it, and an action is the only thing that knows
|
|
15673
|
+
* whether what it just did leaves anything for the surface behind to do.
|
|
15674
|
+
*/
|
|
15675
|
+
actions?: UISurfaceActionEnablement[];
|
|
15676
|
+
/** The values this page is opened with, in author order. See {@link UIPageParam}. */
|
|
15677
|
+
params?: UIPageParam[];
|
|
15678
|
+
};
|
|
15679
|
+
type UIPageParam = {
|
|
15680
|
+
id: string;
|
|
15681
|
+
name: string;
|
|
15682
|
+
type: UIPageParamType;
|
|
15683
|
+
/**
|
|
15684
|
+
* For a `list`: the shape each row has, as a struct id (an engine shape such as `nl.confirmButton`,
|
|
15685
|
+
* a plugin's, or one of the project's). It types the value on both ends - the inputs that give it
|
|
15686
|
+
* and `Get Page Param` - and a list on the page bound to it is checked against it. Absent for
|
|
15687
|
+
* every other type, and for a list of rows of no declared shape.
|
|
15688
|
+
*/
|
|
15689
|
+
struct?: string;
|
|
15690
|
+
/**
|
|
15691
|
+
* What the page reads when whoever opened it gave nothing for this name, in the declared type.
|
|
15692
|
+
* Absent reads as the type's empty value (`uiPageParamDefaultValue`).
|
|
15693
|
+
*/
|
|
15694
|
+
defaultValue?: unknown;
|
|
15695
|
+
};
|
|
15696
|
+
type UIPageParamType = "string" | "text" | "number" | "boolean" | "list" | "json";
|
|
15697
|
+
type UIStageSurface = {
|
|
15698
|
+
id: UISurfaceId;
|
|
15699
|
+
name: string;
|
|
15700
|
+
host: "player";
|
|
15701
|
+
kind: "stageSurface";
|
|
15702
|
+
designSize: UISurfaceDesignSize;
|
|
15703
|
+
rootElementId: UIElementId;
|
|
15704
|
+
settings?: UISurfaceSettings;
|
|
15705
|
+
mount: UIStageSurfaceMount;
|
|
15706
|
+
slots?: Record<string, UISlotDefinition>;
|
|
15707
|
+
/** @see UIAppSurface.actions */
|
|
15708
|
+
actions?: UISurfaceActionEnablement[];
|
|
15709
|
+
};
|
|
15710
|
+
type UISurface = UIAppSurface | UIStageSurface;
|
|
15711
|
+
type UISurfaceDesignSize = {
|
|
15712
|
+
width: number;
|
|
15713
|
+
height: number;
|
|
15714
|
+
};
|
|
15715
|
+
type UISurfaceSettings = {
|
|
15716
|
+
backgroundColor?: string;
|
|
15717
|
+
/**
|
|
15718
|
+
* Painted over {@link backgroundColor}, inside the design area only - the letterbox bars around
|
|
15719
|
+
* a scaled Surface keep showing the colour, so the two settings are not alternatives.
|
|
15720
|
+
*/
|
|
15721
|
+
backgroundImage?: UISurfaceBackgroundImage;
|
|
15722
|
+
pageAnimation?: UIPageAnimationSettings;
|
|
15723
|
+
/**
|
|
15724
|
+
* What the asset set this Surface's own background names resolves to, per locale. Build-written
|
|
15725
|
+
* exactly as {@link UIElement.assetVariants} is, and here rather than on the Surface so the
|
|
15726
|
+
* answer sits beside the only asset a Surface names by itself.
|
|
15727
|
+
*/
|
|
15728
|
+
assetVariants?: AssetVariantMap;
|
|
15729
|
+
};
|
|
15730
|
+
type UISlotDefinition = {
|
|
15731
|
+
id: string;
|
|
15732
|
+
name: string;
|
|
15733
|
+
rootElementId?: UIElementId;
|
|
15734
|
+
};
|
|
15735
|
+
type UIComponentId = string;
|
|
15736
|
+
type UIComponentDefinition = {
|
|
15737
|
+
id: UIComponentId;
|
|
15738
|
+
name: string;
|
|
15739
|
+
rootElementId: UIElementId;
|
|
15740
|
+
elements: Record<UIElementId, UIElement>;
|
|
15741
|
+
/** Declared in the component's own inspector; each instance supplies its own values. */
|
|
15742
|
+
params?: UIComponentParam[];
|
|
15743
|
+
previewMeta?: {
|
|
15744
|
+
width?: number;
|
|
15745
|
+
height?: number;
|
|
15872
15746
|
};
|
|
15747
|
+
createdAt?: string;
|
|
15748
|
+
updatedAt?: string;
|
|
15749
|
+
};
|
|
15750
|
+
type UIComponentParam = {
|
|
15751
|
+
/** Stable identity. Blueprints and instance values reference this, so renaming `name` is free. */
|
|
15752
|
+
id: string;
|
|
15753
|
+
/** What the author called it, shown in the instance's inspector. */
|
|
15754
|
+
name: string;
|
|
15755
|
+
type: UIComponentParamType;
|
|
15756
|
+
/**
|
|
15757
|
+
* Used by an instance that has not overridden it, and by the definition's own preview. For a
|
|
15758
|
+
* `text` parameter these are words written directly, translated through the definition's own unit
|
|
15759
|
+
* (`uiComponentParamUnitId(componentId, paramId)`).
|
|
15760
|
+
*/
|
|
15761
|
+
defaultValue: string;
|
|
15762
|
+
};
|
|
15763
|
+
type UIComponentParamType = "string" | "text" | "audioTrack";
|
|
15764
|
+
type UIElement = {
|
|
15765
|
+
id: UIElementId;
|
|
15766
|
+
type: string;
|
|
15767
|
+
name?: string;
|
|
15768
|
+
parentId: UIElementId | null;
|
|
15769
|
+
childrenIds: UIElementId[];
|
|
15770
|
+
layout: UILayout;
|
|
15771
|
+
style?: UIStyle;
|
|
15772
|
+
props?: Record<string, unknown>;
|
|
15773
|
+
valueBindings?: Record<string, UIElementValueBinding>;
|
|
15774
|
+
/**
|
|
15775
|
+
* How this element arrives and leaves - the same record a Surface uses for its page animation.
|
|
15776
|
+
* Absent means it does neither. Read it through `elementAnimation.ts` rather than directly.
|
|
15777
|
+
*
|
|
15778
|
+
* Its own field rather than a `props` key, because it belongs to the placement and not to the
|
|
15779
|
+
* widget: every type has one, no renderer reads it, and a linked component instance may set it
|
|
15780
|
+
* even though its props come from the definition.
|
|
15781
|
+
*/
|
|
15782
|
+
animation?: UIPageAnimationSettings;
|
|
15783
|
+
extra?: Record<string, unknown>;
|
|
15784
|
+
/**
|
|
15785
|
+
* What each asset set THIS element names resolves to, per locale.
|
|
15786
|
+
*
|
|
15787
|
+
* **Never authored and never on disk under `editor/`.** It is written while a package is being
|
|
15788
|
+
* assembled (`@shared/build/uiAssetSets`) and exists only in the bundle a game runs from, which
|
|
15789
|
+
* is why editing a widget cannot produce one and why version control never sees it.
|
|
15790
|
+
*
|
|
15791
|
+
* It sits on the element rather than in a document-wide table for the reason the story row's
|
|
15792
|
+
* copy gives: a table would enumerate every localized asset the project has, which is the one
|
|
15793
|
+
* thing a protected pack refuses to disclose. One element discloses the variants of the sets
|
|
15794
|
+
* that element itself uses, which is what drawing the widget already discloses.
|
|
15795
|
+
*
|
|
15796
|
+
* Keyed by the set id the props still name - the prop keeps the set id, so a widget whose map is
|
|
15797
|
+
* missing fails loudly at a resolver rather than quietly fetching one language's picture.
|
|
15798
|
+
*/
|
|
15799
|
+
assetVariants?: AssetVariantMap;
|
|
15873
15800
|
};
|
|
15874
|
-
type
|
|
15875
|
-
type
|
|
15876
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
15801
|
+
type UIElementValueBindingValueType = "string" | "json" | "float" | "boolean";
|
|
15802
|
+
type UIElementValueBinding = {
|
|
15803
|
+
kind: "blueprintValue";
|
|
15804
|
+
blueprintId: string;
|
|
15805
|
+
valueType: UIElementValueBindingValueType;
|
|
15806
|
+
}
|
|
15807
|
+
/**
|
|
15808
|
+
* Read one declared field of the list item this element is being drawn for.
|
|
15809
|
+
*
|
|
15810
|
+
* The whole reason the struct model exists. Reaching a row's own data used to take a private
|
|
15811
|
+
* blueprint per element - Get List Item Props into Get JSON Field with the key typed into a
|
|
15812
|
+
* string pin - which is three abstractions deep for "show the title". This is the same read with
|
|
15813
|
+
* none of them: the field is picked from a dropdown, and an element outside an item template
|
|
15814
|
+
* simply has nothing to bind to.
|
|
15815
|
+
*
|
|
15816
|
+
* No `valueType` of its own. The field declares one and the bound prop declares one, and the
|
|
15817
|
+
* conversion between them belongs to the target, not to the binding - the same reason the
|
|
15818
|
+
* slider's binding does not restate that a slider holds a number.
|
|
15819
|
+
*/
|
|
15820
|
+
| {
|
|
15821
|
+
kind: "listItemField";
|
|
15822
|
+
fieldId: string;
|
|
15823
|
+
}
|
|
15824
|
+
/**
|
|
15825
|
+
* Show the value one placement gives a text parameter of the component this element is part of.
|
|
15826
|
+
*
|
|
15827
|
+
* The component's counterpart of `listItemField`, and the same kind of read: no graph, nothing
|
|
15828
|
+
* kept alive - the value is taken from the placement being drawn (`resolveUIComponentTextParams`),
|
|
15829
|
+
* so the editing canvas draws each placement with its own words, and the game translates each
|
|
15830
|
+
* through the placement's own unit or reads the key it names. Only a text parameter answers one,
|
|
15831
|
+
* and only a widget's words take one; outside a placement (the component's own editor) the
|
|
15832
|
+
* element's own words are drawn, as sample text.
|
|
15833
|
+
*/
|
|
15834
|
+
| {
|
|
15835
|
+
kind: "componentParam";
|
|
15836
|
+
paramId: string;
|
|
15837
|
+
}
|
|
15838
|
+
/**
|
|
15839
|
+
* Show the value the page this element is on was opened with for one of its text parameters.
|
|
15840
|
+
*
|
|
15841
|
+
* The page's counterpart of `componentParam`, read the same way: no graph - the value is taken from
|
|
15842
|
+
* the props the page was opened with (`resolveUIPageTextParams`), words given at the opening shown
|
|
15843
|
+
* as given, the declared default translated through the page's own unit. Only a text parameter
|
|
15844
|
+
* answers one, only a widget's words take one, and only on a page: a component's insides and a
|
|
15845
|
+
* Game UI have no page parameters to show. The editing canvas, where the page has not been opened,
|
|
15846
|
+
* draws the default, or the element's own words as sample text while the default is empty.
|
|
15847
|
+
*/
|
|
15848
|
+
| {
|
|
15849
|
+
kind: "pageParam";
|
|
15850
|
+
paramId: string;
|
|
15851
|
+
};
|
|
15852
|
+
type UILayout = {
|
|
15853
|
+
x: number;
|
|
15854
|
+
y: number;
|
|
15855
|
+
width: number;
|
|
15856
|
+
height: number;
|
|
15857
|
+
rotation?: number;
|
|
15858
|
+
opacity?: number;
|
|
15859
|
+
visible?: boolean;
|
|
15860
|
+
/** When true, width and height stay proportional in the inspector and on-canvas resize. */
|
|
15861
|
+
lockAspectRatio?: boolean;
|
|
15862
|
+
};
|
|
15863
|
+
type UIStyle = Record<string, unknown>;
|
|
15864
|
+
type GameTestLogLevel = "verbose" | "info" | "success" | "warning" | "error";
|
|
15865
|
+
type GameTestExitReason =
|
|
15866
|
+
/** Closed from inside the game - the window's own close, or the engine quitting itself. */
|
|
15867
|
+
"closed-by-user"
|
|
15868
|
+
/** The host asked: an explicit stop, or the run being cancelled. */
|
|
15869
|
+
| "stopped-by-host"
|
|
15870
|
+
/** Non-zero exit code, a fatal signal, or an uncaught exception in the game's main process. */
|
|
15871
|
+
| "crashed"
|
|
15872
|
+
/** Never got far enough to run: compile failed, or the runner binary would not spawn. */
|
|
15873
|
+
| "failed-to-start";
|
|
15874
|
+
type GameTestExit = {
|
|
15875
|
+
reason: GameTestExitReason;
|
|
15876
|
+
code: number | null;
|
|
15877
|
+
signal: string | null;
|
|
15878
|
+
};
|
|
15879
|
+
type GameTestChoiceOption = {
|
|
15880
|
+
index: number;
|
|
15881
|
+
text: string;
|
|
15882
|
+
/** Offered but not selectable: the option's `disabledWhen` condition holds. */
|
|
15883
|
+
disabled: boolean;
|
|
15884
|
+
};
|
|
15885
|
+
type GameTestEvent = {
|
|
15886
|
+
kind: "console";
|
|
15887
|
+
level: GameTestLogLevel;
|
|
15888
|
+
source: string;
|
|
15889
|
+
message: string;
|
|
15890
|
+
}
|
|
15891
|
+
/**
|
|
15892
|
+
* An uncaught error inside the running game - `window.onerror` / `unhandledrejection` in its
|
|
15893
|
+
* renderer, `uncaughtException` / `unhandledRejection` in its main process. The game runtime had
|
|
15894
|
+
* no such hook before this pipeline; an uncaught renderer exception was reported nowhere.
|
|
15895
|
+
*/
|
|
15896
|
+
| {
|
|
15897
|
+
kind: "runtime-error";
|
|
15898
|
+
scope: "renderer" | "main";
|
|
15899
|
+
message: string;
|
|
15900
|
+
stack?: string;
|
|
15901
|
+
}
|
|
15902
|
+
/** The engine reached an ending (`event:state.end`), forwarded out of the game's renderer. */
|
|
15903
|
+
| {
|
|
15904
|
+
kind: "game-end";
|
|
15905
|
+
}
|
|
15906
|
+
/**
|
|
15907
|
+
* An `/ending` row ran, naming the ending it declares.
|
|
15908
|
+
*
|
|
15909
|
+
* Beside `game-end` rather than replacing it: `game-end` also fires for a story that simply ran
|
|
15910
|
+
* out of rows, which has no ending to name. A test whose pass condition is "*this* ending" can
|
|
15911
|
+
* only be written against this one.
|
|
15912
|
+
*/
|
|
15913
|
+
| {
|
|
15914
|
+
kind: "ending";
|
|
15915
|
+
endingId: string;
|
|
15916
|
+
name: string;
|
|
15917
|
+
}
|
|
15918
|
+
/**
|
|
15919
|
+
* A choice menu is on screen, with every option it evaluated.
|
|
15920
|
+
*
|
|
15921
|
+
* Fires each time a menu mounts, so a rollback into the same choice reports it again. The list
|
|
15922
|
+
* is what `choose` addresses; see {@link GameTestChoiceOption}.
|
|
15923
|
+
*/
|
|
15924
|
+
| {
|
|
15925
|
+
kind: "choice";
|
|
15926
|
+
options: GameTestChoiceOption[];
|
|
15927
|
+
} | {
|
|
15928
|
+
kind: "exit";
|
|
15929
|
+
exit: GameTestExit;
|
|
15930
|
+
};
|
|
15931
|
+
type GameTestCommand =
|
|
15932
|
+
/**
|
|
15933
|
+
* Begin a story at a scene.
|
|
15934
|
+
*
|
|
15935
|
+
* Necessary because a test session boots to the main app surface - the title screen - and no
|
|
15936
|
+
* story runs until something starts one. The caller supplies the target, exactly as the
|
|
15937
|
+
* blueprint behind a Start button does.
|
|
15938
|
+
*/
|
|
15939
|
+
{
|
|
15940
|
+
kind: "start";
|
|
15941
|
+
storyId: string;
|
|
15942
|
+
sceneId: string;
|
|
15943
|
+
}
|
|
15944
|
+
/** One click on the dialogue: finish the line being typed, or move to the next. */
|
|
15945
|
+
| {
|
|
15946
|
+
kind: "advance";
|
|
15947
|
+
}
|
|
15948
|
+
/** Pick an option of the choice currently on screen, by the index the `choice` event reported. */
|
|
15949
|
+
| {
|
|
15950
|
+
kind: "choose";
|
|
15951
|
+
index: number;
|
|
15952
|
+
};
|
|
15953
|
+
type UITemplateSurfacePlacement = {
|
|
15954
|
+
kind: UISurfaceKind;
|
|
15955
|
+
/** Required for `stageSurface`; the game-UI slot the surface mounts into. */
|
|
15956
|
+
slotId?: UIStageSlotId;
|
|
15957
|
+
};
|
|
15958
|
+
interface AssetTransferManifestEntry {
|
|
15959
|
+
/**
|
|
15960
|
+
* The asset's id in the source project, which the importing project reuses.
|
|
15961
|
+
*
|
|
15962
|
+
* Reusing it is what lets the rows that travelled alongside keep pointing at their files
|
|
15963
|
+
* without being rewritten, and it makes a second paste of the same clipboard a no-op: the id
|
|
15964
|
+
* is already in the library, so there is nothing to import.
|
|
15965
|
+
*/
|
|
15966
|
+
assetId: string;
|
|
15967
|
+
/** Author-facing file name, extension included: what the file is called once imported. */
|
|
15968
|
+
fileName: string;
|
|
15969
|
+
/**
|
|
15970
|
+
* The `AssetType` value as a string.
|
|
15971
|
+
*
|
|
15972
|
+
* Structural for the reason `assetSet.ts` gives for the same field: the enum lives under the
|
|
15973
|
+
* renderer and cannot be imported here.
|
|
15974
|
+
*/
|
|
15975
|
+
type: string;
|
|
15976
|
+
/** Size in bytes, when the offering side already knew it. */
|
|
15977
|
+
size?: number;
|
|
15978
|
+
/**
|
|
15979
|
+
* Whether the payload is a directory rather than a single file.
|
|
15980
|
+
*
|
|
15981
|
+
* True for model bundles, the one asset type whose contents are a tree. It decides both halves
|
|
15982
|
+
* of the transfer: the grant covering the entry reaches everything below the path, and the
|
|
15983
|
+
* importing project copies the tree instead of writing bytes.
|
|
15984
|
+
*/
|
|
15985
|
+
isDirectory?: boolean;
|
|
15986
|
+
}
|
|
15879
15987
|
type PrivilegedWriteBatchEntry = {
|
|
15880
15988
|
path: string;
|
|
15881
15989
|
encoding?: FsTextEncoding;
|
|
@@ -16170,6 +16278,19 @@ export type SearchJumpTarget = {
|
|
|
16170
16278
|
unitId?: string;
|
|
16171
16279
|
storyId?: string;
|
|
16172
16280
|
}
|
|
16281
|
+
/**
|
|
16282
|
+
* A translation table showing its orphans: the translations of one language whose line is no
|
|
16283
|
+
* longer in the game (deleted, or disabled), where they are listed and can be deleted.
|
|
16284
|
+
*/
|
|
16285
|
+
| {
|
|
16286
|
+
kind: "translationOrphans";
|
|
16287
|
+
locale: string;
|
|
16288
|
+
}
|
|
16289
|
+
/** A voice table showing its orphans: the takes of one voice language whose line is no longer in the game. */
|
|
16290
|
+
| {
|
|
16291
|
+
kind: "voiceOrphans";
|
|
16292
|
+
locale: string;
|
|
16293
|
+
}
|
|
16173
16294
|
/** A story motion, open in its own editor. */
|
|
16174
16295
|
| {
|
|
16175
16296
|
kind: "storyMotion";
|
|
@@ -17507,7 +17628,14 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
17507
17628
|
updateElementLayouts(layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
17508
17629
|
mergeKey?: string;
|
|
17509
17630
|
}): void;
|
|
17510
|
-
|
|
17631
|
+
/**
|
|
17632
|
+
* `skipHistory` is for bookkeeping writes nobody asked for - an inspector filling in the
|
|
17633
|
+
* appearance keys an element predates, the moment it is selected. Recorded, that left an undo
|
|
17634
|
+
* step behind every first selection, so Ctrl+Z after looking around a page did nothing visible.
|
|
17635
|
+
*/
|
|
17636
|
+
updateElementProps(elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
17637
|
+
skipHistory?: boolean;
|
|
17638
|
+
}): void;
|
|
17511
17639
|
/**
|
|
17512
17640
|
* Every widget that reads its words from `keyName`, turned into one that holds `words` itself.
|
|
17513
17641
|
*
|
|
@@ -17765,11 +17893,16 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
17765
17893
|
* (`resolveWorkspaceUndoScope`). A definition's own stack holds edits *inside* it, and a deleted
|
|
17766
17894
|
* definition has no tab to press Ctrl+Z in.
|
|
17767
17895
|
*
|
|
17896
|
+
* `scopeId` names another stack for the one library operation that is made from inside a page's
|
|
17897
|
+
* editor - {@link createComponentFromElements}, from the canvas or the outline. The same rule
|
|
17898
|
+
* puts that step on the page's stack: Ctrl+Z pressed where the gesture was made has to take it
|
|
17899
|
+
* back, and on the project's stack it instead undid whatever the page's last edit had been.
|
|
17900
|
+
*
|
|
17768
17901
|
* Each step is a command over whole records, not a snapshot of the library: whichever direction
|
|
17769
17902
|
* runs reads the records as they stand at that moment and writes them back exactly, and nothing
|
|
17770
17903
|
* else in the library is touched. Tagged, so a live session drops these steps with the interface
|
|
17771
17904
|
* editors' stacks (`LiveSessionService`): taking back an addition after a session would remove
|
|
17772
|
-
* whatever the room built inside it.
|
|
17905
|
+
* whatever the room built inside it. (A page's stack is cleared whole when a session starts.)
|
|
17773
17906
|
*/
|
|
17774
17907
|
private pushLibraryStep;
|
|
17775
17908
|
/**
|
|
@@ -18002,8 +18135,13 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18002
18135
|
/**
|
|
18003
18136
|
* A new definition made of copies of elements on a page, with the logic they carry.
|
|
18004
18137
|
*
|
|
18005
|
-
* The page is not changed,
|
|
18006
|
-
*
|
|
18138
|
+
* The page is not changed, but the step that takes this back goes on the page's stack all the
|
|
18139
|
+
* same: it is made from the page's canvas or outline, and that is where Ctrl+Z and the Edit menu
|
|
18140
|
+
* reach (`resolveWorkspaceUndoScope`). On the project's stack - the one {@link createEmptyComponent}
|
|
18141
|
+
* uses, from the rail - Ctrl+Z right after it undid the page's previous edit and left the copy.
|
|
18142
|
+
*
|
|
18143
|
+
* `name` names the definition; without one it takes the element's name, or the catalog's word for
|
|
18144
|
+
* a component when there are several elements or the one has no name.
|
|
18007
18145
|
*/
|
|
18008
18146
|
createComponentFromElements(surfaceId: string, elementIds: string[], name?: string): UIComponentDefinition | null;
|
|
18009
18147
|
/** Recorded in the definition's own undo stack, as a page's name is in the page's. */
|
|
@@ -18098,7 +18236,10 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18098
18236
|
* element on a page has no parameters to show and is left alone.
|
|
18099
18237
|
*/
|
|
18100
18238
|
setElementComponentParamBinding(elementId: string, propPath: string, paramId: string | null): void;
|
|
18101
|
-
|
|
18239
|
+
/** `skipHistory` as {@link updateElementProps} takes it. */
|
|
18240
|
+
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
18241
|
+
skipHistory?: boolean;
|
|
18242
|
+
}): void;
|
|
18102
18243
|
updateComponentElementAnimation(componentId: string, elementId: string, animation: UIPageAnimationSettings | null, options?: {
|
|
18103
18244
|
mergeKey?: string;
|
|
18104
18245
|
}): void;
|
|
@@ -18172,6 +18313,15 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18172
18313
|
private createNotificationStageTemplate;
|
|
18173
18314
|
private createChoiceStageTemplate;
|
|
18174
18315
|
private createNvlStageTemplate;
|
|
18316
|
+
/**
|
|
18317
|
+
* The actions the project's dialogue box reads on with, as far as a new NVL page can follow them.
|
|
18318
|
+
*
|
|
18319
|
+
* Read from the box's graphs the way the engine's own NVL page reads them
|
|
18320
|
+
* (`resolveDialogueAdvanceActionIds`), and kept to actions the project still defines - an
|
|
18321
|
+
* enablement naming a missing one does nothing, and copying it onto a new surface would only
|
|
18322
|
+
* carry the dead entry further.
|
|
18323
|
+
*/
|
|
18324
|
+
private resolveDialogueAdvanceActionsForStarter;
|
|
18175
18325
|
/** Value graph: `Init -> Return Value` fed by `Get List Item Props -> Get JSON Field(propsPath)`. */
|
|
18176
18326
|
private createListItemPropsValueGraph;
|
|
18177
18327
|
/**
|
|
@@ -18186,8 +18336,24 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18186
18336
|
* Panel (`nl.container`) because collection widgets like `nl.nvl.list` do not expose a
|
|
18187
18337
|
* `Mouse Click` head; the panel's own Mouse Click plus Element Click on the interaction layer
|
|
18188
18338
|
* and the list cover every click region.
|
|
18339
|
+
*
|
|
18340
|
+
* The page's wiring when the project has no action its dialogue box reads on with; see
|
|
18341
|
+
* {@link createNvlActionNextGraph} for the one it gets when it has.
|
|
18189
18342
|
*/
|
|
18190
18343
|
private createNvlNextGraph;
|
|
18344
|
+
/**
|
|
18345
|
+
* Event graph for an NVL page in a project whose dialogue box reads on with actions: each of
|
|
18346
|
+
* those actions runs `Next`, and so does a click on the NVL list.
|
|
18347
|
+
*
|
|
18348
|
+
* The actions bring the keys, the gamepad and a click anywhere on the page that is not on a
|
|
18349
|
+
* control - the same presses that read the box on. The list is the exception: it is a control,
|
|
18350
|
+
* and an action stands down over a control (`pointerInputClaimedByControl`), so a click on the
|
|
18351
|
+
* lines would otherwise do nothing. `Element Click` on the list hears exactly that click and no
|
|
18352
|
+
* other, so no press reads on twice and no widget answers a gesture the page answers as well.
|
|
18353
|
+
*
|
|
18354
|
+
* On the surface's blueprint, because only a surface answers an action.
|
|
18355
|
+
*/
|
|
18356
|
+
private createNvlActionNextGraph;
|
|
18191
18357
|
private configureDefaultNotificationBlueprints;
|
|
18192
18358
|
private configureDefaultChoiceBlueprints;
|
|
18193
18359
|
private configureDefaultNvlBlueprints;
|
|
@@ -18899,6 +19065,12 @@ type BlueprintInspectorParamDef = {
|
|
|
18899
19065
|
* dynamically provided options via `dynamicOptionsSource`.
|
|
18900
19066
|
*/
|
|
18901
19067
|
options?: BlueprintInspectorParamSelectOption[];
|
|
19068
|
+
/**
|
|
19069
|
+
* For `kind: "select"`: the options' values are value types, so the card shows each by the type's
|
|
19070
|
+
* word (`formatBlueprintValueTypeLabel`) rather than by its `label`, which stays the English the
|
|
19071
|
+
* command line prints.
|
|
19072
|
+
*/
|
|
19073
|
+
optionsAreValueTypes?: boolean;
|
|
18902
19074
|
/**
|
|
18903
19075
|
* For `kind: "select"` without static `options`: the flow projection
|
|
18904
19076
|
* populates options from context data keyed by this source id.
|
|
@@ -19492,7 +19664,9 @@ interface IUIDocumentService extends IService {
|
|
|
19492
19664
|
updateElementLayouts(layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
19493
19665
|
mergeKey?: string;
|
|
19494
19666
|
}): void;
|
|
19495
|
-
updateElementProps(elementId: string, propsPatch: Record<string, unknown
|
|
19667
|
+
updateElementProps(elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
19668
|
+
skipHistory?: boolean;
|
|
19669
|
+
}): void;
|
|
19496
19670
|
ensureElementBlueprintValueBinding(elementId: string, propPath: string, input: {
|
|
19497
19671
|
valueType: UIElementValueBindingValueType;
|
|
19498
19672
|
displayName?: string;
|
|
@@ -19532,7 +19706,9 @@ interface IUIDocumentService extends IService {
|
|
|
19532
19706
|
updateComponentElementLayouts(componentId: string, layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
19533
19707
|
mergeKey?: string;
|
|
19534
19708
|
}): void;
|
|
19535
|
-
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown
|
|
19709
|
+
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
19710
|
+
skipHistory?: boolean;
|
|
19711
|
+
}): void;
|
|
19536
19712
|
updateComponentElementExtra(componentId: string, elementId: string, extraPatch: Record<string, unknown>): void;
|
|
19537
19713
|
renameComponentElement(componentId: string, elementId: string, name: string): void;
|
|
19538
19714
|
reorderComponentChildren(componentId: string, parentId: string, orderedChildIds: string[]): void;
|