narraleaf-studio 1.4.0 → 1.4.2
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 +1557 -1384
- 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,8 @@ 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";
|
|
3998
2680
|
readonly addKey: "Add";
|
|
3999
2681
|
readonly keyNamePlaceholder: "Key (menu.start\u2026)";
|
|
4000
2682
|
readonly keySourcePlaceholder: "Source text";
|
|
@@ -4037,6 +2719,24 @@ declare const en: {
|
|
|
4037
2719
|
readonly statusReviewed: "Reviewed";
|
|
4038
2720
|
readonly statusStale: "To review";
|
|
4039
2721
|
readonly findPlaceholder: "Find in translations";
|
|
2722
|
+
readonly orphans: {
|
|
2723
|
+
readonly source: "Orphan translations ({count})";
|
|
2724
|
+
readonly summary: {
|
|
2725
|
+
readonly one: "{count} translation belongs to a line that is not in the game";
|
|
2726
|
+
readonly other: "{count} translations belong to lines that are not in the game";
|
|
2727
|
+
};
|
|
2728
|
+
readonly none: "No orphan translations";
|
|
2729
|
+
readonly disabledLine: "Disabled line";
|
|
2730
|
+
readonly deletedLine: "Deleted line";
|
|
2731
|
+
readonly delete: "Delete";
|
|
2732
|
+
readonly deleteAll: "Delete all";
|
|
2733
|
+
readonly deleteConfirm: {
|
|
2734
|
+
readonly one: "Delete {count} orphan translation?";
|
|
2735
|
+
readonly other: "Delete {count} orphan translations?";
|
|
2736
|
+
};
|
|
2737
|
+
readonly deleteDetail: "This cannot be undone. A line that is enabled or restored later needs translating again.";
|
|
2738
|
+
readonly unknown: "A story cannot be read, so orphan translations are not listed.";
|
|
2739
|
+
};
|
|
4040
2740
|
};
|
|
4041
2741
|
readonly live: {
|
|
4042
2742
|
readonly entryClaimed: "{name} is translating this line";
|
|
@@ -4128,6 +2828,21 @@ declare const en: {
|
|
|
4128
2828
|
readonly notePlaceholder: "Note\u2026";
|
|
4129
2829
|
readonly dropHint: "Drop audio to assign";
|
|
4130
2830
|
readonly findPlaceholder: "Find in lines";
|
|
2831
|
+
readonly orphans: {
|
|
2832
|
+
readonly source: "Orphan voice ({count})";
|
|
2833
|
+
readonly summary: {
|
|
2834
|
+
readonly one: "{count} recording is linked to a line that is not in the game";
|
|
2835
|
+
readonly other: "{count} recordings are linked to lines that are not in the game";
|
|
2836
|
+
};
|
|
2837
|
+
readonly none: "No orphan voice";
|
|
2838
|
+
readonly removeAll: "Remove all";
|
|
2839
|
+
readonly removeConfirm: {
|
|
2840
|
+
readonly one: "Remove {count} orphan recording?";
|
|
2841
|
+
readonly other: "Remove {count} orphan recordings?";
|
|
2842
|
+
};
|
|
2843
|
+
readonly removeDetail: "The audio files stay in the asset library. A line that is enabled or restored later needs its audio assigned again.";
|
|
2844
|
+
readonly unknown: "A story cannot be read, so orphan voice is not listed.";
|
|
2845
|
+
};
|
|
4131
2846
|
};
|
|
4132
2847
|
};
|
|
4133
2848
|
readonly textSourceMigration: {
|
|
@@ -6088,6 +4803,9 @@ declare const en: {
|
|
|
6088
4803
|
readonly pip: "Picture-in-picture";
|
|
6089
4804
|
readonly selectRow: "Select a story row to preview its stage state.";
|
|
6090
4805
|
readonly failed: "Preview failed";
|
|
4806
|
+
readonly settleTimeout: "The stage did not finish setting up in time.";
|
|
4807
|
+
readonly gameUiBlueprintError: "Game UI blueprint error: {message}";
|
|
4808
|
+
readonly previousSessionError: "Previous preview session: {message}";
|
|
6091
4809
|
readonly playFromHere: "Play from here";
|
|
6092
4810
|
readonly restart: "Restart";
|
|
6093
4811
|
readonly stop: "Stop playback";
|
|
@@ -6537,6 +5255,7 @@ declare const en: {
|
|
|
6537
5255
|
readonly parallel: "Parallel";
|
|
6538
5256
|
readonly race: "Race, first to finish";
|
|
6539
5257
|
readonly sequence: "In order";
|
|
5258
|
+
readonly noWait: "Doesn't wait";
|
|
6540
5259
|
readonly nvl: "NVL";
|
|
6541
5260
|
readonly menu: "Menu";
|
|
6542
5261
|
readonly option: "Option";
|
|
@@ -6623,6 +5342,7 @@ declare const en: {
|
|
|
6623
5342
|
readonly vfx: "{operation} ambience {name}";
|
|
6624
5343
|
readonly nvl: "NVL block";
|
|
6625
5344
|
readonly blueprint: "Blueprint";
|
|
5345
|
+
readonly blueprintNamed: "Blueprint {name}";
|
|
6626
5346
|
readonly pluginAction: "Plugin action";
|
|
6627
5347
|
readonly cameraOp: {
|
|
6628
5348
|
readonly transform: "Camera";
|
|
@@ -7222,6 +5942,7 @@ declare const en: {
|
|
|
7222
5942
|
};
|
|
7223
5943
|
readonly blueprint: {
|
|
7224
5944
|
readonly storyActionTitle: "Story Action";
|
|
5945
|
+
readonly name: "Blueprint name";
|
|
7225
5946
|
};
|
|
7226
5947
|
readonly audio: {
|
|
7227
5948
|
readonly track: "Track";
|
|
@@ -7327,13 +6048,9 @@ declare const en: {
|
|
|
7327
6048
|
readonly parallel: "Parallel";
|
|
7328
6049
|
readonly race: "Race";
|
|
7329
6050
|
readonly repeat: "Repeat";
|
|
7330
|
-
readonly
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
readonly all: "All";
|
|
7334
|
-
readonly allAsync: "All async";
|
|
7335
|
-
readonly any: "Any";
|
|
7336
|
-
};
|
|
6051
|
+
readonly rowsAfter: "Rows after";
|
|
6052
|
+
readonly rowsAfterWait: "Start when the group finishes";
|
|
6053
|
+
readonly rowsAfterStart: "Start with the group";
|
|
7337
6054
|
readonly times: "Times";
|
|
7338
6055
|
readonly loopKind: "Loop";
|
|
7339
6056
|
readonly loopKindTimes: "A number of times";
|
|
@@ -8127,9 +6844,9 @@ declare const en: {
|
|
|
8127
6844
|
};
|
|
8128
6845
|
readonly graph: {
|
|
8129
6846
|
readonly noNodes: "Graph has no nodes yet.";
|
|
8130
|
-
readonly pinMultipleInput: "Pin {
|
|
8131
|
-
readonly pinMultipleOutput: "Pin {
|
|
8132
|
-
readonly entryMissingNode: "
|
|
6847
|
+
readonly pinMultipleInput: "Pin \"{pin}\" has multiple input connections; a pin takes one connection.";
|
|
6848
|
+
readonly pinMultipleOutput: "Pin \"{pin}\" has multiple output connections; a pin takes one connection.";
|
|
6849
|
+
readonly entryMissingNode: "The entry points to a node that does not exist.";
|
|
8133
6850
|
readonly entryInvalid: "Function entry not found.";
|
|
8134
6851
|
};
|
|
8135
6852
|
readonly event: {
|
|
@@ -8142,21 +6859,21 @@ declare const en: {
|
|
|
8142
6859
|
};
|
|
8143
6860
|
readonly edge: {
|
|
8144
6861
|
readonly selfConnection: "Node \"{node}\" cannot connect to itself.";
|
|
8145
|
-
readonly fromUnknown: "
|
|
8146
|
-
readonly toUnknown: "
|
|
6862
|
+
readonly fromUnknown: "A wire starts at a node that does not exist.";
|
|
6863
|
+
readonly toUnknown: "A wire ends at a node that does not exist.";
|
|
8147
6864
|
readonly portMismatch: "Pin mismatch on {from} \u2192 {to}.";
|
|
8148
6865
|
readonly connectionInvalid: "Invalid connection {from} -> {to}.{detail}";
|
|
8149
6866
|
readonly connectionTypeDetail: " Type mismatch: {from} -> {to}.";
|
|
8150
6867
|
};
|
|
8151
6868
|
readonly node: {
|
|
8152
|
-
readonly contextInvalid: "Node \"{name}\" is not allowed in
|
|
6869
|
+
readonly contextInvalid: "Node \"{name}\" is not allowed in {owner}.{hint}";
|
|
8153
6870
|
readonly contextValueReturnHint: " Return Value only belongs in Blueprint Value graphs.";
|
|
8154
6871
|
readonly contextListItemHint: " It reads the list row it runs for, and nothing here draws one.";
|
|
8155
|
-
readonly noRuntime: "Node \"{node}\"
|
|
6872
|
+
readonly noRuntime: "Node \"{node}\" has no runtime.";
|
|
8156
6873
|
readonly inputMissing: "\"{node}\" has nothing connected to \"{pin}\".";
|
|
8157
6874
|
readonly stepLimit: "\"{head}\" stopped at \"{node}\" after {steps} steps without a wait.";
|
|
8158
6875
|
readonly stepLimitGlobal: "\"{head}\" in App logic stopped at \"{node}\" after {steps} steps without a wait.";
|
|
8159
|
-
readonly unknownType: "
|
|
6876
|
+
readonly unknownType: "Unknown node type \"{type}\". Its plugin may be uninstalled or disabled.";
|
|
8160
6877
|
readonly variableIdInvalid: "Node \"{node}\": pick a variable.";
|
|
8161
6878
|
readonly persistentVariableIdInvalid: "Node \"{node}\": pick a persistent variable.";
|
|
8162
6879
|
readonly savedVariableIdInvalid: "Node \"{node}\": pick a saved variable.";
|
|
@@ -8167,11 +6884,11 @@ declare const en: {
|
|
|
8167
6884
|
readonly pageParamMissing: "\"{node}\": the page does not declare this param.";
|
|
8168
6885
|
};
|
|
8169
6886
|
readonly fn: {
|
|
8170
|
-
readonly nameMissing: "
|
|
6887
|
+
readonly nameMissing: "\"{node}\": set a function name.";
|
|
8171
6888
|
readonly duplicateName: "Fn name \"{name}\" is used by another function in scope.";
|
|
8172
6889
|
readonly returnOrphan: "Fn Return must be reachable from a Fn head.";
|
|
8173
6890
|
readonly returnMultipleHeads: "Fn Return is reachable from multiple Fn heads.";
|
|
8174
|
-
readonly returnSignatureConflict: "
|
|
6891
|
+
readonly returnSignatureConflict: "\"{node}\" declares different results than the first Return of this fn.";
|
|
8175
6892
|
readonly callUnset: "Node \"{node}\": pick a function.";
|
|
8176
6893
|
readonly callTargetNotFound: "Fn \"{name}\" does not exist in this scope.";
|
|
8177
6894
|
readonly callSignatureStale: "Function signature changed; re-select \"{name}\" to update its pins.";
|
|
@@ -8438,6 +7155,8 @@ declare const en: {
|
|
|
8438
7155
|
readonly removeOutput: "Remove output pin";
|
|
8439
7156
|
readonly jsonFieldName: "JSON object field name";
|
|
8440
7157
|
readonly readsRow: "Current row";
|
|
7158
|
+
readonly newParamName: "param{n}";
|
|
7159
|
+
readonly newResultName: "result{n}";
|
|
8441
7160
|
};
|
|
8442
7161
|
readonly comment: {
|
|
8443
7162
|
readonly color: {
|
|
@@ -9939,6 +8658,9 @@ declare const en: {
|
|
|
9939
8658
|
readonly type: "Type";
|
|
9940
8659
|
readonly typeString: "String";
|
|
9941
8660
|
readonly typeText: "Text";
|
|
8661
|
+
readonly typeAudioTrack: "Audio track";
|
|
8662
|
+
readonly trackMissing: "Missing track";
|
|
8663
|
+
readonly trackNone: "No track";
|
|
9942
8664
|
readonly bindLabel: "Parameter";
|
|
9943
8665
|
readonly bindNone: "No parameter";
|
|
9944
8666
|
readonly bindMissing: "Missing parameter";
|
|
@@ -10046,6 +8768,7 @@ declare const en: {
|
|
|
10046
8768
|
readonly unknown: "Usage could not be determined";
|
|
10047
8769
|
readonly unknownDetail: "{location} could not be read";
|
|
10048
8770
|
readonly unknownComputed: "{location} picks its asset by a name assembled at run time";
|
|
8771
|
+
readonly defaultFonts: "Default fonts";
|
|
10049
8772
|
readonly count: {
|
|
10050
8773
|
readonly one: "{count} reference";
|
|
10051
8774
|
readonly other: "{count} references";
|
|
@@ -10415,6 +9138,7 @@ declare const en: {
|
|
|
10415
9138
|
};
|
|
10416
9139
|
readonly row: {
|
|
10417
9140
|
readonly nameAria: "Variable name";
|
|
9141
|
+
readonly defaultName: "Variable";
|
|
10418
9142
|
readonly defaultPlaceholder: "default";
|
|
10419
9143
|
readonly defaultAria: "Default value";
|
|
10420
9144
|
readonly delete: "Delete variable";
|
|
@@ -10516,6 +9240,8 @@ declare const en: {
|
|
|
10516
9240
|
readonly history: {
|
|
10517
9241
|
readonly moveSurface: "move interface {name}";
|
|
10518
9242
|
readonly setEntryPage: "set entry page {name}";
|
|
9243
|
+
readonly setRootElement: "set root element {name}";
|
|
9244
|
+
readonly wrapRoot: "wrap {name} in a container";
|
|
10519
9245
|
readonly createComponent: "create component {name}";
|
|
10520
9246
|
readonly duplicateComponent: "duplicate component {name}";
|
|
10521
9247
|
readonly duplicateComponents: "duplicate {count} components";
|
|
@@ -10625,6 +9351,9 @@ declare const en: {
|
|
|
10625
9351
|
readonly canvas: {
|
|
10626
9352
|
readonly unknownWidget: "Unknown widget";
|
|
10627
9353
|
readonly widgetRenderFailed: "Widget failed to draw";
|
|
9354
|
+
readonly componentMissing: "Missing component";
|
|
9355
|
+
readonly componentLoopBlocked: "Component loop blocked";
|
|
9356
|
+
readonly componentRootMissing: "Component root missing";
|
|
10628
9357
|
readonly wordsFromRowField: "These words come from the list row field \u201C{field}\u201D.";
|
|
10629
9358
|
readonly wordsFromBlueprintValue: "These words come from the blueprint value \u201C{name}\u201D.";
|
|
10630
9359
|
readonly wordsFromComponentParam: "These words come from the component parameter \u201C{param}\u201D, set on each placement.";
|
|
@@ -10687,6 +9416,14 @@ declare const en: {
|
|
|
10687
9416
|
readonly showSelected: "Show selected";
|
|
10688
9417
|
readonly hideSelected: "Hide selected";
|
|
10689
9418
|
readonly addToComponentLibrary: "Add to Component Library";
|
|
9419
|
+
readonly setAsRoot: "Set as root element";
|
|
9420
|
+
readonly wrapRootInContainer: "Wrap in container";
|
|
9421
|
+
readonly setAsRootNotContainer: "Available when the root element is a container";
|
|
9422
|
+
readonly setAsRootNotAlone: "Available when this is the only element in the root container";
|
|
9423
|
+
readonly setAsRootNotFree: "Available when the root container's child layout is Free";
|
|
9424
|
+
readonly addedToComponentLibrary: "Added \u201C{name}\u201D to the Component Library.";
|
|
9425
|
+
readonly cannotHoldChildren: "{name} cannot contain child elements";
|
|
9426
|
+
readonly linkedInstanceContents: "The contents of a linked instance are edited in its component";
|
|
10690
9427
|
readonly group: "Group";
|
|
10691
9428
|
readonly ungroup: "Ungroup";
|
|
10692
9429
|
readonly addState: "Add state";
|
|
@@ -11804,6 +10541,8 @@ declare const en: {
|
|
|
11804
10541
|
readonly ending: {
|
|
11805
10542
|
readonly title: "Page shown when the story ends";
|
|
11806
10543
|
readonly none: "Show nothing";
|
|
10544
|
+
readonly unset: "Not chosen";
|
|
10545
|
+
readonly missing: "Deleted page";
|
|
11807
10546
|
};
|
|
11808
10547
|
readonly usedBy: {
|
|
11809
10548
|
readonly one: "Used by {count} reference";
|
|
@@ -12007,6 +10746,11 @@ declare const en: {
|
|
|
12007
10746
|
readonly one: "{count} track under it moves to {parent}.";
|
|
12008
10747
|
readonly other: "{count} tracks under it move to {parent}.";
|
|
12009
10748
|
};
|
|
10749
|
+
readonly builtinName: {
|
|
10750
|
+
readonly bgm: "Music";
|
|
10751
|
+
readonly sound: "SFX";
|
|
10752
|
+
readonly voice: "Voice";
|
|
10753
|
+
};
|
|
12010
10754
|
readonly slider: {
|
|
12011
10755
|
readonly bgm: "Music volume";
|
|
12012
10756
|
readonly sound: "SFX volume";
|
|
@@ -13197,7 +11941,7 @@ declare const en: {
|
|
|
13197
11941
|
readonly rootName: "Root";
|
|
13198
11942
|
readonly componentName: "Component";
|
|
13199
11943
|
readonly pageName: "Page";
|
|
13200
|
-
readonly
|
|
11944
|
+
readonly nameCopy: "{name} Copy";
|
|
13201
11945
|
readonly speaker: "Speaker";
|
|
13202
11946
|
readonly dialog: {
|
|
13203
11947
|
readonly interactionLayer: "Dialog Interaction Layer";
|
|
@@ -13384,6 +12128,7 @@ declare const en: {
|
|
|
13384
12128
|
readonly contentLabel: "Variant the content comes from";
|
|
13385
12129
|
readonly contentHint: "The scenes, variant conditions and art of this variant go into the patch.";
|
|
13386
12130
|
readonly sameVariant: "The content and the build it updates are the same variant. The patch carries no changes.";
|
|
12131
|
+
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.";
|
|
13387
12132
|
readonly outputLabel: "Save as";
|
|
13388
12133
|
readonly nameLabel: "Name";
|
|
13389
12134
|
readonly namePlaceholder: "Shown in the game's log";
|
|
@@ -14342,6 +13087,7 @@ declare const en: {
|
|
|
14342
13087
|
};
|
|
14343
13088
|
readonly message: {
|
|
14344
13089
|
readonly ruleFailed: "{rule} could not run";
|
|
13090
|
+
readonly listSeparator: ", ";
|
|
14345
13091
|
readonly storyLoadFailed: "{story} could not be opened";
|
|
14346
13092
|
readonly storyTooOld: "{story} is in story format v{version}, and this Studio opens v{minimum} and later";
|
|
14347
13093
|
readonly storyTooNew: "{story} was written by a newer NarraLeaf Studio (story format v{version}); this Studio reads up to v{supported}";
|
|
@@ -15228,13 +13974,14 @@ declare const en: {
|
|
|
15228
13974
|
readonly finding: {
|
|
15229
13975
|
readonly sceneUnreachable: "No path can satisfy the conditions leading to \"{scene}\"";
|
|
15230
13976
|
readonly optionUnreachable: "No path can satisfy the condition for offering \"{option}\"";
|
|
13977
|
+
readonly conditionUnreachable: "No path can satisfy \"{condition}\"";
|
|
15231
13978
|
readonly branchUnreachable: "No path can satisfy the condition for taking this branch";
|
|
15232
13979
|
readonly endingUnreachable: "\"{name}\" is written but no path can satisfy the conditions to reach it";
|
|
15233
13980
|
readonly endingUnreachableUnnamed: "This ending is written but no path can satisfy the conditions to reach it";
|
|
15234
13981
|
};
|
|
15235
13982
|
readonly summary: {
|
|
15236
13983
|
readonly passed: "Everything the script leads to can be reached";
|
|
15237
|
-
readonly failed: "Unreachable: scenes {scenes}, options {options}, endings {endings}";
|
|
13984
|
+
readonly failed: "Unreachable: scenes {scenes}, options {options}, condition branches {conditions}, endings {endings}";
|
|
15238
13985
|
};
|
|
15239
13986
|
};
|
|
15240
13987
|
readonly reachableEndings: {
|
|
@@ -15667,6 +14414,19 @@ declare const en: {
|
|
|
15667
14414
|
};
|
|
15668
14415
|
readonly preview: "Preview in";
|
|
15669
14416
|
readonly excluded: "Not used for {language}";
|
|
14417
|
+
readonly builtin: {
|
|
14418
|
+
readonly group: "Built-in fonts";
|
|
14419
|
+
readonly systemUi: "System UI";
|
|
14420
|
+
readonly sansSerif: "Sans-serif (generic)";
|
|
14421
|
+
readonly serif: "Serif (generic)";
|
|
14422
|
+
readonly monospace: "Monospace (generic)";
|
|
14423
|
+
readonly kind: {
|
|
14424
|
+
readonly system: "System font";
|
|
14425
|
+
readonly sansSerif: "Sans-serif";
|
|
14426
|
+
readonly serif: "Serif";
|
|
14427
|
+
readonly monospace: "Monospace";
|
|
14428
|
+
};
|
|
14429
|
+
};
|
|
15670
14430
|
};
|
|
15671
14431
|
readonly panel: {
|
|
15672
14432
|
readonly add: "Add color";
|
|
@@ -15794,6 +14554,10 @@ declare const en: {
|
|
|
15794
14554
|
readonly detail: "It is running in another browser tab. Close that tab, then reload this page.";
|
|
15795
14555
|
readonly reload: "Reload";
|
|
15796
14556
|
};
|
|
14557
|
+
readonly words: {
|
|
14558
|
+
readonly option: "Option";
|
|
14559
|
+
readonly unknownSpeaker: "Unknown";
|
|
14560
|
+
};
|
|
15797
14561
|
};
|
|
15798
14562
|
readonly crash: {
|
|
15799
14563
|
readonly screen: {
|
|
@@ -15836,36 +14600,1366 @@ declare const en: {
|
|
|
15836
14600
|
readonly quit: "Quit";
|
|
15837
14601
|
};
|
|
15838
14602
|
};
|
|
15839
|
-
readonly documents: {
|
|
15840
|
-
readonly tooNew: {
|
|
15841
|
-
readonly message: "{subject} was written by a newer NarraLeaf Studio ({kind} format v{version}); this build reads up to v{supported}";
|
|
15842
|
-
readonly kind: {
|
|
15843
|
-
readonly story: "story";
|
|
15844
|
-
readonly storyIndex: "story library";
|
|
15845
|
-
readonly storyAnimation: "animation";
|
|
15846
|
-
readonly uiDocument: "interface";
|
|
15847
|
-
readonly uiGraphs: "interface blueprint";
|
|
15848
|
-
readonly blueprints: "blueprint";
|
|
15849
|
-
readonly variables: "variable";
|
|
15850
|
-
readonly saveSchema: "save schema";
|
|
15851
|
-
readonly localization: "translation";
|
|
15852
|
-
readonly localizationKeys: "translation key";
|
|
15853
|
-
readonly voice: "voice";
|
|
15854
|
-
readonly brand: "design";
|
|
15855
|
-
readonly appTags: "variant";
|
|
15856
|
-
readonly dlc: "DLC";
|
|
15857
|
-
readonly assetSets: "asset set";
|
|
15858
|
-
readonly audioTracks: "audio track";
|
|
15859
|
-
readonly characters: "character";
|
|
15860
|
-
};
|
|
15861
|
-
};
|
|
14603
|
+
readonly documents: {
|
|
14604
|
+
readonly tooNew: {
|
|
14605
|
+
readonly message: "{subject} was written by a newer NarraLeaf Studio ({kind} format v{version}); this build reads up to v{supported}";
|
|
14606
|
+
readonly kind: {
|
|
14607
|
+
readonly story: "story";
|
|
14608
|
+
readonly storyIndex: "story library";
|
|
14609
|
+
readonly storyAnimation: "animation";
|
|
14610
|
+
readonly uiDocument: "interface";
|
|
14611
|
+
readonly uiGraphs: "interface blueprint";
|
|
14612
|
+
readonly blueprints: "blueprint";
|
|
14613
|
+
readonly variables: "variable";
|
|
14614
|
+
readonly saveSchema: "save schema";
|
|
14615
|
+
readonly localization: "translation";
|
|
14616
|
+
readonly localizationKeys: "translation key";
|
|
14617
|
+
readonly voice: "voice";
|
|
14618
|
+
readonly brand: "design";
|
|
14619
|
+
readonly appTags: "variant";
|
|
14620
|
+
readonly dlc: "DLC";
|
|
14621
|
+
readonly assetSets: "asset set";
|
|
14622
|
+
readonly audioTracks: "audio track";
|
|
14623
|
+
readonly characters: "character";
|
|
14624
|
+
};
|
|
14625
|
+
};
|
|
14626
|
+
};
|
|
14627
|
+
};
|
|
14628
|
+
type Messages = typeof en;
|
|
14629
|
+
type Flatten<T, Prefix extends string = ""> = {
|
|
14630
|
+
[K in keyof T & string]: T[K] extends string ? `${Prefix}${K}` : Flatten<T[K], `${Prefix}${K}.`>;
|
|
14631
|
+
}[keyof T & string];
|
|
14632
|
+
type TranslationKey = Flatten<Messages> | (string & {});
|
|
14633
|
+
type AudioClipRegion = {
|
|
14634
|
+
/** Offset from the start of the clip, in milliseconds. Where playback starts. */
|
|
14635
|
+
inMs?: number;
|
|
14636
|
+
/** Offset from the start of the clip, in milliseconds. Where playback stops, or where a loop turns around. */
|
|
14637
|
+
outMs?: number;
|
|
14638
|
+
/**
|
|
14639
|
+
* Offset from the start of the clip, in milliseconds. Where each repeat returns to.
|
|
14640
|
+
*
|
|
14641
|
+
* Absent means "return to {@link inMs}" - a plain loop, and exactly what every record written
|
|
14642
|
+
* before this field existed means. Set past the in point it describes the standard VN
|
|
14643
|
+
* **intro→loop**: `inMs..loopStartMs` plays once, then `loopStartMs..outMs` repeats forever.
|
|
14644
|
+
*
|
|
14645
|
+
* Constrained to `inMs <= loopStartMs < outMs`. A value outside that window is dropped rather
|
|
14646
|
+
* than clamped: clamping would invent a loop point the author never marked, while dropping
|
|
14647
|
+
* degrades to the plain loop the two other markers already describe.
|
|
14648
|
+
*/
|
|
14649
|
+
loopStartMs?: number;
|
|
14650
|
+
/**
|
|
14651
|
+
* Playback gain in decibels, never above 0: the engine cannot raise a clip past its own level, so
|
|
14652
|
+
* balancing loudness means bringing the loud clips down. Absent is unity. Folded into a volume
|
|
14653
|
+
* only by {@link clipVolume}.
|
|
14654
|
+
*/
|
|
14655
|
+
gainDb?: number;
|
|
14656
|
+
};
|
|
14657
|
+
type StoredAudioClipRegion = Pick<AudioClipRegion, "inMs" | "outMs" | "loopStartMs">;
|
|
14658
|
+
type StoredAudioGain = {
|
|
14659
|
+
db: number;
|
|
14660
|
+
};
|
|
14661
|
+
type BlueprintOpenExternalRequest = {
|
|
14662
|
+
url: string;
|
|
14663
|
+
};
|
|
14664
|
+
type BlueprintOpenExternalOutcome = "opened" | "refused" | "failed";
|
|
14665
|
+
type BlueprintOpenExternalResult = {
|
|
14666
|
+
outcome: BlueprintOpenExternalOutcome;
|
|
14667
|
+
/** Human-readable reason, null when the page was handed over. */
|
|
14668
|
+
error: string | null;
|
|
14669
|
+
};
|
|
14670
|
+
type BlueprintGraphKind = "event" | "function" | "macro";
|
|
14671
|
+
type GameBuildPlatform = "windows" | "macos" | "linux" | "web" | "android" | "ios";
|
|
14672
|
+
declare const UI_STRUCT_FIELD_TYPES: readonly [
|
|
14673
|
+
"string",
|
|
14674
|
+
"number",
|
|
14675
|
+
"boolean",
|
|
14676
|
+
"image",
|
|
14677
|
+
"color",
|
|
14678
|
+
"json"
|
|
14679
|
+
];
|
|
14680
|
+
type UIStructFieldType = (typeof UI_STRUCT_FIELD_TYPES)[number];
|
|
14681
|
+
type UIStructField = {
|
|
14682
|
+
/**
|
|
14683
|
+
* Stable identity. Value bindings, node params and generated pins all address a field by this,
|
|
14684
|
+
* so renaming `key` costs nothing and reshuffling the list costs nothing.
|
|
14685
|
+
*/
|
|
14686
|
+
id: string;
|
|
14687
|
+
/** The property name this field occupies inside an item object. */
|
|
14688
|
+
key: string;
|
|
14689
|
+
/**
|
|
14690
|
+
* What the author called it, shown in the item table header and in every field picker.
|
|
14691
|
+
*
|
|
14692
|
+
* Optional because `key` is usually already the readable name; a label is what lets a field
|
|
14693
|
+
* whose key has to stay ASCII (it travels in JSON to a plugin, a save file, a story variable)
|
|
14694
|
+
* still read as words in the editor.
|
|
14695
|
+
*/
|
|
14696
|
+
label?: string;
|
|
14697
|
+
type: UIStructFieldType;
|
|
14698
|
+
};
|
|
14699
|
+
type UIStructId = string;
|
|
14700
|
+
type UIStructDef = {
|
|
14701
|
+
id: UIStructId;
|
|
14702
|
+
fields: UIStructField[];
|
|
14703
|
+
};
|
|
14704
|
+
type PluginPermissionPersistence = "temporary" | "permanent";
|
|
14705
|
+
type PluginFileSystemPermissionMode = "read" | "write" | "readwrite";
|
|
14706
|
+
/**
|
|
14707
|
+
* Install-time permissions split into two families, and the split is the whole
|
|
14708
|
+
* point:
|
|
14709
|
+
*
|
|
14710
|
+
* - **Author-declared** (`filesystem`, `api`) — privileged Studio controls the
|
|
14711
|
+
* plugin asks for explicitly in `permissions[]`.
|
|
14712
|
+
* - **Derived** (`runtime`, `sidecar`, `buildDependency`, `externalLink`,
|
|
14713
|
+
* `network`) — computed from `contributes` by {@link validatePluginManifest}
|
|
14714
|
+
* and *rejected* if written by
|
|
14715
|
+
* hand. A capability is declared in exactly one place, so what the prompt shows
|
|
14716
|
+
* and what the plugin can actually reach cannot drift apart. Adding a
|
|
14717
|
+
* capability widens the permission set, which makes the update re-prompt for
|
|
14718
|
+
* free (see `isPermissionSubset`).
|
|
14719
|
+
*/
|
|
14720
|
+
export type PluginInstallPermission = {
|
|
14721
|
+
kind: "filesystem";
|
|
14722
|
+
path: string;
|
|
14723
|
+
mode: PluginFileSystemPermissionMode;
|
|
14724
|
+
recursive: boolean;
|
|
14725
|
+
} | {
|
|
14726
|
+
kind: "api";
|
|
14727
|
+
capability: string;
|
|
14728
|
+
} | {
|
|
14729
|
+
/** Derived from `contributes.runtimeCapabilities`. */
|
|
14730
|
+
kind: "runtime";
|
|
14731
|
+
capability: PluginRuntimeCapability;
|
|
14732
|
+
} | {
|
|
14733
|
+
/** Derived from `contributes.sidecars`: a native child process shipped inside the author's game. */
|
|
14734
|
+
kind: "sidecar";
|
|
14735
|
+
id: string;
|
|
14736
|
+
/**
|
|
14737
|
+
* Which of the two sidecar shapes this is, carried through from the manifest because the
|
|
14738
|
+
* two are different promises: `executable` starts a separate binary, `node` runs the
|
|
14739
|
+
* plugin's own JavaScript under the game's Electron as Node. An author told only "this
|
|
14740
|
+
* ships a helper program" has not been told which one they are approving.
|
|
14741
|
+
*
|
|
14742
|
+
* Named apart from `kind` above on purpose - that one says which permission this is, this
|
|
14743
|
+
* one says what the sidecar runs.
|
|
14744
|
+
*/
|
|
14745
|
+
sidecarKind: PluginSidecarKind;
|
|
14746
|
+
/** `<platform>-<arch>` keys the sidecar ships binaries for. */
|
|
14747
|
+
platforms: string[];
|
|
14748
|
+
} | {
|
|
14749
|
+
/** Derived from `contributes.buildDependencies`: binaries downloaded at build time. */
|
|
14750
|
+
kind: "buildDependency";
|
|
14751
|
+
id: string;
|
|
14752
|
+
/** Distinct hostnames the binaries are fetched from. */
|
|
14753
|
+
hosts: string[];
|
|
14754
|
+
} | {
|
|
14755
|
+
/**
|
|
14756
|
+
* Derived from `contributes.externalLinks`: addresses outside the game this plugin may open
|
|
14757
|
+
* in the player's browser or platform handler.
|
|
14758
|
+
*
|
|
14759
|
+
* One permission carrying every pattern rather than one permission each, because what the
|
|
14760
|
+
* author is deciding is a single question - "may this plugin send the player out to these
|
|
14761
|
+
* places" - and a prompt that asked it four times would be four chances to stop reading.
|
|
14762
|
+
* `isPermissionSubset` still compares the patterns one by one, so adding one to a later
|
|
14763
|
+
* version widens the set and re-prompts.
|
|
14764
|
+
*
|
|
14765
|
+
* The patterns are the author's own strings, unrewritten: this list is what the prompt
|
|
14766
|
+
* shows, and normalizing a permission before showing it to the person approving it would
|
|
14767
|
+
* make the prompt and the manifest two slightly different documents.
|
|
14768
|
+
*/
|
|
14769
|
+
kind: "externalLink";
|
|
14770
|
+
patterns: string[];
|
|
14771
|
+
} | {
|
|
14772
|
+
/**
|
|
14773
|
+
* Derived from `contributes.network`: hosts this plugin's runtime code requests bytes from.
|
|
14774
|
+
*
|
|
14775
|
+
* A separate question from `externalLink` and never folded into it. Opening a page hands an
|
|
14776
|
+
* address to the browser the player already uses and nothing comes back; this fetches, and
|
|
14777
|
+
* what comes back runs inside the game. An author deciding about one has not decided about
|
|
14778
|
+
* the other.
|
|
14779
|
+
*
|
|
14780
|
+
* The patterns are also what a project's network allowlist shows attributed to this plugin,
|
|
14781
|
+
* which is the point of declaring them: a build narrowed to a list still reaches what the
|
|
14782
|
+
* author approved here, and the list can say so rather than silently making room.
|
|
14783
|
+
*/
|
|
14784
|
+
kind: "network";
|
|
14785
|
+
patterns: string[];
|
|
14786
|
+
};
|
|
14787
|
+
type PluginSidecarKind = "executable" | "node";
|
|
14788
|
+
declare const PluginRuntimeCapability: {
|
|
14789
|
+
/** `app.game.store` — plugin-scoped persistent key/value storage. */
|
|
14790
|
+
readonly Store: "store";
|
|
14791
|
+
/** `app.game.events` — game lifecycle and engine event subscription. */
|
|
14792
|
+
readonly Events: "events";
|
|
14793
|
+
/** `app.game.state.get` — read story variables. */
|
|
14794
|
+
readonly StateRead: "state.read";
|
|
14795
|
+
/** `app.game.state.set` — write story variables. */
|
|
14796
|
+
readonly StateWrite: "state.write";
|
|
14797
|
+
/** `app.game.saves` — list and read save metadata. */
|
|
14798
|
+
readonly SavesRead: "saves.read";
|
|
14799
|
+
/**
|
|
14800
|
+
* `app.game.saves.write` / `.load` — overwrite a save slot and load one.
|
|
14801
|
+
*
|
|
14802
|
+
* Separate from (and heavier than) `saves.read`: this can destroy a
|
|
14803
|
+
* playthrough. It exists because quick-save/quick-load is an ordinary thing
|
|
14804
|
+
* for a visual novel to ship — the built-in Quick Save plugin is exactly
|
|
14805
|
+
* this — and a capability model that cannot express a feature the product
|
|
14806
|
+
* already has would just push authors back around it.
|
|
14807
|
+
*/
|
|
14808
|
+
readonly SavesWrite: "saves.write";
|
|
14809
|
+
/** `app.game.ui.overlay` — draw on top of the game. */
|
|
14810
|
+
readonly UiOverlay: "ui.overlay";
|
|
14811
|
+
/** `app.game.assets` — resolve packaged asset URLs. */
|
|
14812
|
+
readonly Assets: "assets";
|
|
14813
|
+
/**
|
|
14814
|
+
* `app.game.locale` — read and observe the game language, and read the project's own
|
|
14815
|
+
* localized text through the same table and fallback chain the `Get Text` node uses.
|
|
14816
|
+
*/
|
|
14817
|
+
readonly Locale: "locale";
|
|
14818
|
+
/**
|
|
14819
|
+
* `app.game.menu` — own the menu bar above the game.
|
|
14820
|
+
*
|
|
14821
|
+
* Its own capability rather than part of anything else, because what it grants is *the chrome
|
|
14822
|
+
* the player reads the game through*: every row on that bar, and what each one does, is this
|
|
14823
|
+
* plugin's to decide. The rows still act through the game's own vocabulary (see
|
|
14824
|
+
* `@shared/types/gameMenu`) - nothing here reaches past what a blueprint could already do - but
|
|
14825
|
+
* an author approving this is handing over a surface their players will read as the game's own,
|
|
14826
|
+
* which is worth a name at install rather than arriving with something else.
|
|
14827
|
+
*
|
|
14828
|
+
* Absent on shells with no bar to give (the web export, Dev Mode), which is reported the way
|
|
14829
|
+
* every environment difference is: the member is not there.
|
|
14830
|
+
*/
|
|
14831
|
+
readonly Menu: "menu";
|
|
14832
|
+
/**
|
|
14833
|
+
* `app.game.story` — register a compile pass that observes each scene and injects engine
|
|
14834
|
+
* actions around its rows.
|
|
14835
|
+
*
|
|
14836
|
+
* The heaviest of the ten to read, and the one whose weight is easiest to miss: nothing here
|
|
14837
|
+
* touches the player's files, and the author still sees only their own document — but a pass
|
|
14838
|
+
* runs over *every* scene of the project, sees who speaks in each, and can put actions around
|
|
14839
|
+
* lines it did not write. That is a plugin editing the story, at the last moment before it
|
|
14840
|
+
* plays, and it is worth a name the author approves at install.
|
|
14841
|
+
*
|
|
14842
|
+
* It is not `state.write` in disguise and does not imply it: a pass builds actions, it does not
|
|
14843
|
+
* run them, and the one piece of runtime state it can touch is a scene-local flag the compiler
|
|
14844
|
+
* creates and owns (`SceneCompileContext.runtimeFlag`), never a story variable the author
|
|
14845
|
+
* declared.
|
|
14846
|
+
*/
|
|
14847
|
+
readonly StoryCompile: "story.compile";
|
|
14848
|
+
/**
|
|
14849
|
+
* `app.game.diagnostics` — read what the player's caches are holding.
|
|
14850
|
+
*
|
|
14851
|
+
* The lightest of the list to grant and the easiest to misread as heavier than it is. Nothing
|
|
14852
|
+
* here is authored data: it reports how many images the engine is holding, how many bytes those
|
|
14853
|
+
* cost, and what budgets they are being held against. It reads no story, no save and no
|
|
14854
|
+
* variable, and every member is a read - there is nothing to set.
|
|
14855
|
+
*
|
|
14856
|
+
* It is its own capability rather than free like `data` and `config` because a plugin that
|
|
14857
|
+
* watches memory is a plugin that runs continuously and reports numbers about the player's
|
|
14858
|
+
* machine, and an author should see that named at install. The performance inspector is the
|
|
14859
|
+
* plugin this exists for.
|
|
14860
|
+
*
|
|
14861
|
+
* Absent until a game session is live, which is the same rule every other engine-backed member
|
|
14862
|
+
* follows: a plugin's `setup()` runs during boot, and asking then answers null rather than
|
|
14863
|
+
* throwing.
|
|
14864
|
+
*/
|
|
14865
|
+
readonly Diagnostics: "diagnostics";
|
|
14866
|
+
/**
|
|
14867
|
+
* `app.game.process.memory()` — read how much memory the game's processes hold, as the
|
|
14868
|
+
* operating system counts it.
|
|
14869
|
+
*
|
|
14870
|
+
* Its own capability rather than a member of `diagnostics`, because what it reports is not the
|
|
14871
|
+
* game's: `diagnostics` describes the engine's caches, which are the game's own bookkeeping,
|
|
14872
|
+
* while this is the player's machine - how much of its memory every process of the game is
|
|
14873
|
+
* holding, the main process and the GPU process included. An author who approved the first did
|
|
14874
|
+
* not approve the second, and a plugin that already holds `diagnostics` must not gain this by
|
|
14875
|
+
* an update without being asked again - which is what a capability of its own guarantees.
|
|
14876
|
+
*
|
|
14877
|
+
* Read-only, and numbers only: sizes and what each process is for, never a path, an id or a
|
|
14878
|
+
* command line. Nothing is sent anywhere by the host; what a plugin does with a reading is
|
|
14879
|
+
* visible in the code the author installed.
|
|
14880
|
+
*
|
|
14881
|
+
* Absent where there are no processes to count - the web export - and narrowed in Dev Mode to
|
|
14882
|
+
* the window's own renderer, since everything around it is Studio's.
|
|
14883
|
+
*/
|
|
14884
|
+
readonly ProcessMemory: "process.memory";
|
|
14885
|
+
};
|
|
14886
|
+
type PluginRuntimeCapability = typeof PluginRuntimeCapability[keyof typeof PluginRuntimeCapability];
|
|
14887
|
+
/**
|
|
14888
|
+
* `app.game.sidecar` has no entry here on purpose: it exists exactly when
|
|
14889
|
+
* `contributes.sidecars` is non-empty. Declaring the sidecar *is* the request.
|
|
14890
|
+
*/
|
|
14891
|
+
export interface PluginIdentity {
|
|
14892
|
+
id: string;
|
|
14893
|
+
name?: string;
|
|
14894
|
+
version?: string;
|
|
14895
|
+
publisher?: string;
|
|
14896
|
+
}
|
|
14897
|
+
interface PluginPermissionRequestBase {
|
|
14898
|
+
requestId: string;
|
|
14899
|
+
plugin: PluginIdentity;
|
|
14900
|
+
reason?: string;
|
|
14901
|
+
requestedAt?: number;
|
|
14902
|
+
}
|
|
14903
|
+
export type PluginPermissionRequest = (PluginPermissionRequestBase & {
|
|
14904
|
+
kind: "trust";
|
|
14905
|
+
persistence?: PluginPermissionPersistence;
|
|
14906
|
+
}) | (PluginPermissionRequestBase & {
|
|
14907
|
+
kind: "filesystem";
|
|
14908
|
+
path: string;
|
|
14909
|
+
mode: PluginFileSystemPermissionMode;
|
|
14910
|
+
recursive: boolean;
|
|
14911
|
+
persistence: PluginPermissionPersistence;
|
|
14912
|
+
}) | (PluginPermissionRequestBase & {
|
|
14913
|
+
kind: "install";
|
|
14914
|
+
source: string;
|
|
14915
|
+
permissions?: PluginInstallPermission[];
|
|
14916
|
+
persistence?: PluginPermissionPersistence;
|
|
14917
|
+
}) | (PluginPermissionRequestBase & {
|
|
14918
|
+
kind: "api";
|
|
14919
|
+
capability: string;
|
|
14920
|
+
persistence?: PluginPermissionPersistence;
|
|
14921
|
+
});
|
|
14922
|
+
interface PluginPermissionGrantResult {
|
|
14923
|
+
requestId: string;
|
|
14924
|
+
pluginId: string;
|
|
14925
|
+
kind: PluginPermissionRequest["kind"];
|
|
14926
|
+
approved: boolean;
|
|
14927
|
+
persistence: PluginPermissionPersistence;
|
|
14928
|
+
grantedAt?: number;
|
|
14929
|
+
}
|
|
14930
|
+
export type PluginPermissionPromptResult = PluginPermissionGrantResult | null;
|
|
14931
|
+
declare const PluginManifestVersion = 2;
|
|
14932
|
+
/**
|
|
14933
|
+
* Per-target plugin entry files. Each entry is a prebundled ESM file relative
|
|
14934
|
+
* to the plugin package root. At least one target must be declared.
|
|
14935
|
+
*
|
|
14936
|
+
* - `studio`: loaded in the workspace window only; talks to the
|
|
14937
|
+
* `narraleaf-studio/plugin` host API (editor extensions).
|
|
14938
|
+
* - `runtime`: loaded in every game execution environment (Dev Mode window,
|
|
14939
|
+
* Preview, Production); talks to the `narraleaf-studio/runtime` host API
|
|
14940
|
+
* (game logic such as blueprint node execute bindings).
|
|
14941
|
+
*/
|
|
14942
|
+
export type PluginManifestEntries = {
|
|
14943
|
+
studio?: string;
|
|
14944
|
+
runtime?: string;
|
|
14945
|
+
};
|
|
14946
|
+
type PluginLocaleContribution = {
|
|
14947
|
+
code: string;
|
|
14948
|
+
nativeName?: string;
|
|
14949
|
+
englishName?: string;
|
|
14950
|
+
intl?: string;
|
|
14951
|
+
dir?: "ltr" | "rtl";
|
|
14952
|
+
messages: string;
|
|
14953
|
+
};
|
|
14954
|
+
type PluginBinaryPlatformKey = string;
|
|
14955
|
+
type PluginSidecarTargetContribution = {
|
|
14956
|
+
/** Executable (or, for `kind: "node"`, the .js file) to run. Must appear in `include`. */
|
|
14957
|
+
entry: string;
|
|
14958
|
+
/**
|
|
14959
|
+
* Everything shipped for this platform. Entries are package-relative paths,
|
|
14960
|
+
* or `dep:<buildDependencyId>/<path>` to pull an artifact produced by a
|
|
14961
|
+
* declared build dependency (that is how third-party redistributables that
|
|
14962
|
+
* we may not vendor ourselves reach the pack).
|
|
14963
|
+
*/
|
|
14964
|
+
include: string[];
|
|
14965
|
+
/**
|
|
14966
|
+
* `sha256` of every package-relative entry in `include`, hex. Verified at
|
|
14967
|
+
* install and again at pack time, so a tampered package fails to install
|
|
14968
|
+
* instead of silently shipping a different binary. `dep:` entries are covered
|
|
14969
|
+
* by the build dependency's own digest instead.
|
|
14970
|
+
*/
|
|
14971
|
+
sha256: Record<string, string>;
|
|
14972
|
+
};
|
|
14973
|
+
type PluginSidecarContribution = {
|
|
14974
|
+
/** Prefixed with the plugin id, like every other contributed identifier. */
|
|
14975
|
+
id: string;
|
|
14976
|
+
/**
|
|
14977
|
+
* `executable` spawns the binary directly and talks to it over stdin/stdout; `node` runs the
|
|
14978
|
+
* .js as an Electron utility process and talks to it over `process.parentPort`, which is the
|
|
14979
|
+
* one channel a utility process has - it has no stdin.
|
|
14980
|
+
*/
|
|
14981
|
+
kind: PluginSidecarKind;
|
|
14982
|
+
/**
|
|
14983
|
+
* Newline-delimited JSON, which is the framing and not the channel: where the frames travel is
|
|
14984
|
+
* {@link kind}'s business, and only an executable sidecar's travel over stdio. Either way
|
|
14985
|
+
* stderr stays a plain log channel.
|
|
14986
|
+
*
|
|
14987
|
+
* `stdio-jsonl` is the older spelling of the same thing, from when there was only one channel
|
|
14988
|
+
* it could mean. Manifests that say it keep working and always will - a published plugin is a
|
|
14989
|
+
* file somebody already shipped - but a new one should say `jsonl`.
|
|
14990
|
+
*/
|
|
14991
|
+
transport: "jsonl" | "stdio-jsonl";
|
|
14992
|
+
/** `onGameStart` spawns with the window; `onRequest` waits for the first call. */
|
|
14993
|
+
autostart: "onGameStart" | "onRequest";
|
|
14994
|
+
/** How long the handshake may take before the sidecar counts as unavailable. */
|
|
14995
|
+
startupTimeoutMs: number;
|
|
14996
|
+
/** Grace period between the shutdown message and SIGTERM. */
|
|
14997
|
+
shutdownTimeoutMs: number;
|
|
14998
|
+
restart: {
|
|
14999
|
+
maxRetries: number;
|
|
15000
|
+
backoffMs: number;
|
|
15001
|
+
};
|
|
15002
|
+
targets: Record<PluginBinaryPlatformKey, PluginSidecarTargetContribution>;
|
|
15003
|
+
};
|
|
15004
|
+
type PluginBuildDependencyTargetContribution = {
|
|
15005
|
+
url: string;
|
|
15006
|
+
/** Mandatory. Doubles as the cache key, so re-pointing the URL at identical bytes never re-downloads. */
|
|
15007
|
+
sha256: string;
|
|
15008
|
+
archive: "zip";
|
|
15009
|
+
/** Archive-internal path -> path inside the produced dependency directory. */
|
|
15010
|
+
files: Record<string, string>;
|
|
15011
|
+
} | {
|
|
15012
|
+
url: string;
|
|
15013
|
+
sha256: string;
|
|
15014
|
+
archive: "none";
|
|
15015
|
+
/** Name the downloaded file takes inside the produced dependency directory. */
|
|
15016
|
+
fileName: string;
|
|
15017
|
+
};
|
|
15018
|
+
type PluginBuildDependencyContribution = {
|
|
15019
|
+
/** Prefixed with the plugin id. Referenced from sidecar `include` as `dep:<id>/<path>`. */
|
|
15020
|
+
id: string;
|
|
15021
|
+
/** Shown to the author at install and in build logs; say what the binaries are. */
|
|
15022
|
+
description?: string;
|
|
15023
|
+
targets: Record<PluginBinaryPlatformKey, PluginBuildDependencyTargetContribution>;
|
|
15024
|
+
};
|
|
15025
|
+
declare const PLUGIN_BUILD_CONFIG_TYPES: readonly [
|
|
15026
|
+
"text",
|
|
15027
|
+
"secret"
|
|
15028
|
+
];
|
|
15029
|
+
type PluginBuildConfigType = (typeof PLUGIN_BUILD_CONFIG_TYPES)[number];
|
|
15030
|
+
declare const PLUGIN_BUILD_CONFIG_SCOPES: readonly [
|
|
15031
|
+
"global",
|
|
15032
|
+
"variant",
|
|
15033
|
+
"platform",
|
|
15034
|
+
"variant-platform"
|
|
15035
|
+
];
|
|
15036
|
+
type PluginBuildConfigScope = (typeof PLUGIN_BUILD_CONFIG_SCOPES)[number];
|
|
15037
|
+
type PluginBuildConfigFieldContribution = {
|
|
15038
|
+
/** Unique within the plugin. Keys the stored value; never displayed. */
|
|
15039
|
+
key: string;
|
|
15040
|
+
/** Author-facing name of the field. */
|
|
15041
|
+
label: string;
|
|
15042
|
+
/** One line saying what the value is for. */
|
|
15043
|
+
description?: string;
|
|
15044
|
+
/** `text` is stored in the project; `secret` is not - the project stores a handle. */
|
|
15045
|
+
type: PluginBuildConfigType;
|
|
15046
|
+
/** Which builds share one value. */
|
|
15047
|
+
scope: PluginBuildConfigScope;
|
|
15048
|
+
/** Platforms the field applies to. Absent means every platform. */
|
|
15049
|
+
platforms?: GameBuildPlatform[];
|
|
15050
|
+
/** A build that would ship without a value for this field is refused. */
|
|
15051
|
+
required?: boolean;
|
|
15052
|
+
};
|
|
15053
|
+
export type PluginContributes = {
|
|
15054
|
+
/** Blueprint node types this plugin provides (editor def + runtime execute). */
|
|
15055
|
+
blueprintNodes?: string[];
|
|
15056
|
+
/** Widget element types this plugin provides (editor module + runtime renderer). */
|
|
15057
|
+
widgets?: string[];
|
|
15058
|
+
/** Studio language packs: locales this plugin adds or fills. */
|
|
15059
|
+
locales?: PluginLocaleContribution[];
|
|
15060
|
+
/**
|
|
15061
|
+
* Plugin storage namespaces to publish with the game, readable at runtime
|
|
15062
|
+
* through `app.game.data.readJson(namespace)`.
|
|
15063
|
+
*
|
|
15064
|
+
* Plugin stores live under the project's `editor/` directory, which is never
|
|
15065
|
+
* packaged. A plugin whose runtime needs authored data (catalogs, tables)
|
|
15066
|
+
* must list those namespaces here. The list is an explicit allowlist so
|
|
15067
|
+
* editor-only plugin state cannot leak into a shipped game by accident.
|
|
15068
|
+
*/
|
|
15069
|
+
runtimeData?: string[];
|
|
15070
|
+
/**
|
|
15071
|
+
* Test ids this plugin registers with `app.services.tests` (see
|
|
15072
|
+
* `docs/plugin-test-protocol.md`). Declaring them lets the Launcher say what
|
|
15073
|
+
* a plugin checks before any of its code runs, and registering an
|
|
15074
|
+
* undeclared id throws at load.
|
|
15075
|
+
*
|
|
15076
|
+
* Unlike every other code-backed contribution here, this one derives no
|
|
15077
|
+
* install permission — a test only ever executes because the author picked
|
|
15078
|
+
* it out of the Run > Test dialog and pressed Start, so there is no ambient
|
|
15079
|
+
* capability to consent to at install time.
|
|
15080
|
+
*/
|
|
15081
|
+
tests?: string[];
|
|
15082
|
+
/**
|
|
15083
|
+
* Save ids the plugin keeps for its own bookkeeping - a quick-save slot, a checkpoint - rather
|
|
15084
|
+
* than slots a player chose.
|
|
15085
|
+
*
|
|
15086
|
+
* The game leaves them out of `List Saves`, and so out of every save screen built on it, the way
|
|
15087
|
+
* it leaves out its own autosave ring. Nothing else changes for them: the plugin still writes,
|
|
15088
|
+
* loads and finds them through `app.game.saves` (whose `listIds` is the raw store listing and
|
|
15089
|
+
* still includes them), and the explicit-id save nodes - `Load Save`, `Delete Save`, the
|
|
15090
|
+
* `Get Save` readers - take them like any other id.
|
|
15091
|
+
*
|
|
15092
|
+
* Prefixed with the plugin id like the type lists above, so a plugin can only reserve ids in its
|
|
15093
|
+
* own namespace and can never hide a slot the author's own `Save Game` wrote. That is also why it
|
|
15094
|
+
* derives no install permission: it reaches nothing the plugin did not already own.
|
|
15095
|
+
*/
|
|
15096
|
+
reservedSaveIds?: string[];
|
|
15097
|
+
/**
|
|
15098
|
+
* Capability domains the `runtime` entry may use. Each maps 1:1 onto a
|
|
15099
|
+
* namespace on `app.game`, and an undeclared domain is absent from that
|
|
15100
|
+
* object rather than present-and-throwing — so what the install prompt
|
|
15101
|
+
* listed and what the plugin can reach are the same set by construction.
|
|
15102
|
+
*/
|
|
15103
|
+
runtimeCapabilities?: PluginRuntimeCapability[];
|
|
15104
|
+
/** Native child processes shipped inside the author's game. */
|
|
15105
|
+
sidecars?: PluginSidecarContribution[];
|
|
15106
|
+
/** External binaries fetched (and cached) at build time. */
|
|
15107
|
+
buildDependencies?: PluginBuildDependencyContribution[];
|
|
15108
|
+
/**
|
|
15109
|
+
* Values the author supplies before a build can ship. Declaration only - see
|
|
15110
|
+
* {@link PluginBuildConfigFieldContribution} for why there is no registration API and why this
|
|
15111
|
+
* grants the plugin nothing.
|
|
15112
|
+
*/
|
|
15113
|
+
buildConfig?: PluginBuildConfigFieldContribution[];
|
|
15114
|
+
/**
|
|
15115
|
+
* Address patterns this plugin may hand to the player's browser or platform handler, through
|
|
15116
|
+
* `app.game.navigation.openExternal`.
|
|
15117
|
+
*
|
|
15118
|
+
* Unlike everything else here, this one is *not* a list of identifiers the plugin owns - it is a
|
|
15119
|
+
* list of places outside the game it may reach, so it derives an install permission the author
|
|
15120
|
+
* approves by name (see `PluginInstallPermission` `kind: "externalLink"`) and adding one to a
|
|
15121
|
+
* later version re-prompts.
|
|
15122
|
+
*
|
|
15123
|
+
* Patterns are matched structurally, never as strings: see `isExternalLinkPatternDeclared` in
|
|
15124
|
+
* `@shared/types/externalLinkPattern` for the exact semantics. Wildcards are allowed
|
|
15125
|
+
* (`https://*.example.com/app/*`) and the scheme is not restricted to `http(s)`, because the
|
|
15126
|
+
* storefronts a plugin integrates with speak their own (`steam://run/480`). A handful of schemes
|
|
15127
|
+
* are refused at validation whatever a manifest says; that list and its reasoning live in the
|
|
15128
|
+
* same module.
|
|
15129
|
+
*
|
|
15130
|
+
* This is separate from, and grants nothing towards, {@link network}: opening a page and
|
|
15131
|
+
* fetching bytes are different acts, and the Open Link node the author writes is a third thing
|
|
15132
|
+
* again, governed only by its scheme.
|
|
15133
|
+
*/
|
|
15134
|
+
externalLinks?: string[];
|
|
15135
|
+
/**
|
|
15136
|
+
* Address patterns this plugin's runtime code requests bytes from.
|
|
15137
|
+
*
|
|
15138
|
+
* Like {@link externalLinks} and unlike everything else here, this is a list of places outside
|
|
15139
|
+
* the game rather than identifiers the plugin owns, so it derives an install permission the
|
|
15140
|
+
* author approves by name (`PluginInstallPermission` `kind: "network"`) and adding one to a
|
|
15141
|
+
* later version re-prompts.
|
|
15142
|
+
*
|
|
15143
|
+
* Two rules narrower than the external-link patterns, both for the same reason - what comes
|
|
15144
|
+
* back from a fetch runs inside the game, while an opened page does not:
|
|
15145
|
+
*
|
|
15146
|
+
* - **`http(s)` only.** There is no storefront-scheme case here; a plugin that fetches speaks
|
|
15147
|
+
* HTTP.
|
|
15148
|
+
* - **The path is written out.** `https://api.example.com/*` is a declaration and
|
|
15149
|
+
* `https://api.example.com` is not, because the pattern language reads the second as the
|
|
15150
|
+
* single path `/`. It is refused rather than rewritten: this string is what the install
|
|
15151
|
+
* prompt shows, and a manifest whose text differs from what was approved is the one thing
|
|
15152
|
+
* this whole mechanism exists to prevent.
|
|
15153
|
+
*
|
|
15154
|
+
* What this reaches is *not* widened by the project's own network allowlist and does not widen
|
|
15155
|
+
* it. A build narrowed to a list still reaches these hosts - the author approved them at
|
|
15156
|
+
* install - and the list shows them attributed to this plugin rather than quietly making room.
|
|
15157
|
+
*/
|
|
15158
|
+
network?: string[];
|
|
15159
|
+
/**
|
|
15160
|
+
* The props of a contributed widget that hold words a player reads, by widget type.
|
|
15161
|
+
*
|
|
15162
|
+
* Each type named here must also be in {@link widgets}. A declared prop is treated as Studio's own
|
|
15163
|
+
* text and button labels are: the properties panel offers its words written directly or read
|
|
15164
|
+
* from a translation key, the canvas draws a key's words, the words go into the project's
|
|
15165
|
+
* translation table and exports, the project checks report a missing translation or key, and
|
|
15166
|
+
* the glyph check reads them. In the game the widget's renderer is handed the words already in
|
|
15167
|
+
* the player's language, in the prop it declared - it never resolves a key itself.
|
|
15168
|
+
*
|
|
15169
|
+
* Declared in the manifest rather than at registration because both entries need it - the
|
|
15170
|
+
* studio entry's widget is edited by it and the runtime entry's is drawn by it - and a
|
|
15171
|
+
* declaration only one of them made would be the half that drifts.
|
|
15172
|
+
*/
|
|
15173
|
+
widgetText?: Record<string, PluginWidgetTextContribution[]>;
|
|
15174
|
+
/**
|
|
15175
|
+
* The shapes of the rows this plugin's nodes hand out, so a list can be given one and a field
|
|
15176
|
+
* read from one by name.
|
|
15177
|
+
*
|
|
15178
|
+
* A node pin carrying them says `struct:<id>` or `array<struct:<id>>` - `Get Gallery` answers
|
|
15179
|
+
* `array<struct:narraleaf.gallery.entry>` - and from then on the plugin's rows are what the
|
|
15180
|
+
* engine's own are: a list picks the shape under Edit content and its fields are the plugin's
|
|
15181
|
+
* and locked, Get Field offers them, and a list handed rows that lack them is reported.
|
|
15182
|
+
*
|
|
15183
|
+
* Declared in the manifest rather than at registration because the editor, the running game and
|
|
15184
|
+
* the command-line checker all need the shape, and only the manifest is read by all three before
|
|
15185
|
+
* any plugin code runs. Ids must start with the plugin id.
|
|
15186
|
+
*/
|
|
15187
|
+
structs?: PluginStructContribution[];
|
|
15188
|
+
};
|
|
15189
|
+
type PluginStructContribution = {
|
|
15190
|
+
/** Prefixed with the plugin id: `narraleaf.gallery.entry`. */
|
|
15191
|
+
id: string;
|
|
15192
|
+
/** What the editor calls the shape - "Gallery entry". */
|
|
15193
|
+
name: string;
|
|
15194
|
+
/** `name` per editor locale, matched exactly as `localized` names are. */
|
|
15195
|
+
localized?: Record<string, string>;
|
|
15196
|
+
/** In the order the rows are read: the list's content editor shows them in this order. */
|
|
15197
|
+
fields: {
|
|
15198
|
+
key: string;
|
|
15199
|
+
type: UIStructFieldType;
|
|
15200
|
+
}[];
|
|
15201
|
+
};
|
|
15202
|
+
/**
|
|
15203
|
+
* One prop of a contributed widget whose value is words a player reads.
|
|
15204
|
+
*
|
|
15205
|
+
* `keyProp` is the prop the widget's translation key is kept in when the author reads the words from
|
|
15206
|
+
* one. It defaults to `<prop>LocalizationKey`, and it is the host's to write: the widget never reads it
|
|
15207
|
+
* to draw, because the words in `prop` already come from the key when there is one.
|
|
15208
|
+
*/
|
|
15209
|
+
export type PluginWidgetTextContribution = {
|
|
15210
|
+
/** The prop holding the words. */
|
|
15211
|
+
prop: string;
|
|
15212
|
+
/** The prop the translation key is kept in; `<prop>LocalizationKey` when not declared. */
|
|
15213
|
+
keyProp?: string;
|
|
15214
|
+
/** What the properties panel calls the words. The prop name when not declared. */
|
|
15215
|
+
label?: string;
|
|
15216
|
+
/** `label` per editor locale, matched exactly as `localized` names are. */
|
|
15217
|
+
localized?: Record<string, string>;
|
|
15218
|
+
/** Whether the words may run over several lines; one line when not declared. */
|
|
15219
|
+
multiline?: boolean;
|
|
15220
|
+
};
|
|
15221
|
+
/**
|
|
15222
|
+
* The plugin's name and description in one editor language. Either may be left out, and the plain
|
|
15223
|
+
* manifest field is shown in its place.
|
|
15224
|
+
*/
|
|
15225
|
+
export type PluginManifestLocalizedText = {
|
|
15226
|
+
name?: string;
|
|
15227
|
+
description?: string;
|
|
15228
|
+
};
|
|
15229
|
+
/**
|
|
15230
|
+
* `name` and `description` per editor locale, keyed by the exact code Studio's language setting
|
|
15231
|
+
* stores (`zh`, `ja`, or a code a language pack adds). There is no region matching: an entry under
|
|
15232
|
+
* `zh-CN` is never shown, because no Studio locale is called that.
|
|
15233
|
+
*/
|
|
15234
|
+
export type PluginManifestLocalized = Record<string, PluginManifestLocalizedText>;
|
|
15235
|
+
export type PluginManifestV2 = Omit<PluginIdentity, "id" | "name" | "version"> & Required<Pick<PluginIdentity, "id" | "name" | "version">> & {
|
|
15236
|
+
manifestVersion: typeof PluginManifestVersion;
|
|
15237
|
+
description?: string;
|
|
15238
|
+
/**
|
|
15239
|
+
* Translations of `name` and `description`, shown wherever Studio names the plugin to an author
|
|
15240
|
+
* whose editor is set to that language. The plain fields stay the plugin's identity: command
|
|
15241
|
+
* lines, project files and built games name it by them.
|
|
15242
|
+
*/
|
|
15243
|
+
localized?: PluginManifestLocalized;
|
|
15244
|
+
/**
|
|
15245
|
+
* Package-relative path to the thumbnail shown beside the plugin's name in
|
|
15246
|
+
* the Launcher list. Square, at most 512x512, and one of the extensions in
|
|
15247
|
+
* `PLUGIN_ICON_EXTENSIONS`. Plugins without one keep the name monogram.
|
|
15248
|
+
*/
|
|
15249
|
+
icon?: string;
|
|
15250
|
+
entries: PluginManifestEntries;
|
|
15251
|
+
contributes?: PluginContributes;
|
|
15252
|
+
permissions?: PluginInstallPermission[];
|
|
15253
|
+
};
|
|
15254
|
+
export type NormalizedPluginManifestV2 = PluginManifestV2 & {
|
|
15255
|
+
contributes: Required<PluginContributes>;
|
|
15256
|
+
permissions: PluginInstallPermission[];
|
|
15257
|
+
};
|
|
15258
|
+
type PluginInstallSource = {
|
|
15259
|
+
kind: "local-directory";
|
|
15260
|
+
path: string;
|
|
15261
|
+
} | {
|
|
15262
|
+
kind: "builtin";
|
|
15263
|
+
path: string;
|
|
15264
|
+
} | {
|
|
15265
|
+
kind: "registry";
|
|
15266
|
+
url: string;
|
|
15267
|
+
};
|
|
15268
|
+
export type PluginInstallRecord = {
|
|
15269
|
+
pluginId: string;
|
|
15270
|
+
installPath: string;
|
|
15271
|
+
enabled: boolean;
|
|
15272
|
+
builtIn: boolean;
|
|
15273
|
+
manifest: NormalizedPluginManifestV2;
|
|
15274
|
+
installSource: PluginInstallSource;
|
|
15275
|
+
installedAt: number;
|
|
15276
|
+
updatedAt: number;
|
|
15277
|
+
grantedManifestVersion?: string | null;
|
|
15278
|
+
/**
|
|
15279
|
+
* The permission set the user actually approved, recorded so a later version
|
|
15280
|
+
* that does not widen it can inherit the grant instead of re-prompting.
|
|
15281
|
+
* Absent on records written before this was tracked — the manifest at
|
|
15282
|
+
* `grantedManifestVersion` is the fallback source of truth for those.
|
|
15283
|
+
*/
|
|
15284
|
+
grantedPermissions?: PluginInstallPermission[] | null;
|
|
15285
|
+
lastError?: string | null;
|
|
15286
|
+
};
|
|
15287
|
+
type PluginStatus = "enabled" | "disabled" | "needsAuthorization" | "error";
|
|
15288
|
+
export type PluginListItem = {
|
|
15289
|
+
pluginId: string;
|
|
15290
|
+
manifest: NormalizedPluginManifestV2;
|
|
15291
|
+
/**
|
|
15292
|
+
* `app://` address of the declared icon, absent when the plugin ships none.
|
|
15293
|
+
* Resolved here rather than in the renderer because only the main process
|
|
15294
|
+
* knows where the package landed on disk.
|
|
15295
|
+
*/
|
|
15296
|
+
iconUrl?: string;
|
|
15297
|
+
installPath: string;
|
|
15298
|
+
enabled: boolean;
|
|
15299
|
+
builtIn: boolean;
|
|
15300
|
+
status: PluginStatus;
|
|
15301
|
+
installSource: PluginInstallSource;
|
|
15302
|
+
installedAt: number;
|
|
15303
|
+
updatedAt: number;
|
|
15304
|
+
grantedManifestVersion?: string | null;
|
|
15305
|
+
lastError?: string | null;
|
|
15306
|
+
};
|
|
15307
|
+
/** Descriptor handed to the workspace window: plugins with a studio entry. */
|
|
15308
|
+
export type WorkspacePluginDescriptor = {
|
|
15309
|
+
plugin: PluginIdentity;
|
|
15310
|
+
manifest: NormalizedPluginManifestV2;
|
|
15311
|
+
entryUrl: string;
|
|
15312
|
+
};
|
|
15313
|
+
/** Descriptor handed to game execution environments: plugins with a runtime entry. */
|
|
15314
|
+
export type RuntimePluginDescriptor = {
|
|
15315
|
+
plugin: PluginIdentity;
|
|
15316
|
+
manifest: NormalizedPluginManifestV2;
|
|
15317
|
+
entryUrl: string;
|
|
15318
|
+
/**
|
|
15319
|
+
* Published plugin storage, keyed by the namespaces declared in
|
|
15320
|
+
* `contributes.runtimeData`. Absent namespaces simply have no entry - a
|
|
15321
|
+
* plugin must tolerate missing data (the project may never have written it).
|
|
15322
|
+
*/
|
|
15323
|
+
data?: Record<string, unknown>;
|
|
15324
|
+
/**
|
|
15325
|
+
* This plugin's own `contributes.buildConfig` values, as the build that produced the pack
|
|
15326
|
+
* resolved them, keyed by field key. Only ever the declaring plugin's own values: the pack
|
|
15327
|
+
* holds one record per plugin and a descriptor is built from one entry.
|
|
15328
|
+
*
|
|
15329
|
+
* Absent outside a compiled pack - the editor and Dev Mode hand out descriptors that were never
|
|
15330
|
+
* built for a variant, so there is nothing resolved to carry.
|
|
15331
|
+
*/
|
|
15332
|
+
buildConfig?: Record<string, string>;
|
|
15333
|
+
};
|
|
15334
|
+
type GameMenuLabel = {
|
|
15335
|
+
readonly key?: string | null;
|
|
15336
|
+
readonly text: string;
|
|
15337
|
+
readonly words?: string | null;
|
|
15338
|
+
};
|
|
15339
|
+
declare const GAME_MENU_DYNAMIC_SOURCES: readonly [
|
|
15340
|
+
"textLanguage",
|
|
15341
|
+
"voiceLanguage",
|
|
15342
|
+
"windowScale"
|
|
15343
|
+
];
|
|
15344
|
+
type GameMenuDynamicSource = typeof GAME_MENU_DYNAMIC_SOURCES[number];
|
|
15345
|
+
type GameMenuAction =
|
|
15346
|
+
/** Open an app surface as a page. */
|
|
15347
|
+
{
|
|
15348
|
+
readonly type: "openPage";
|
|
15349
|
+
readonly surfaceId: string;
|
|
15350
|
+
}
|
|
15351
|
+
/** Open an app surface as a layer over whatever is on screen. */
|
|
15352
|
+
| {
|
|
15353
|
+
readonly type: "openLayer";
|
|
15354
|
+
readonly surfaceId: string;
|
|
15355
|
+
readonly modal?: boolean;
|
|
15356
|
+
readonly dismissible?: boolean;
|
|
15357
|
+
readonly group?: string | null;
|
|
15358
|
+
}
|
|
15359
|
+
/** End the playthrough and hand the screen to a page - the same act as the story's quit row. */
|
|
15360
|
+
| {
|
|
15361
|
+
readonly type: "quitToPage";
|
|
15362
|
+
readonly surfaceId: string;
|
|
15363
|
+
}
|
|
15364
|
+
/**
|
|
15365
|
+
* Close the game.
|
|
15366
|
+
*
|
|
15367
|
+
* The same act as the Quit Application node, which means the same terms: a graceful quit that
|
|
15368
|
+
* does NOT run the author's close handler (`On Window Close Requested` answers the window
|
|
15369
|
+
* frame's own close, and the node deliberately stands the guard aside so a quit the game asked
|
|
15370
|
+
* for is not asked back about). An author who wants their own "really quit?" screen binds a
|
|
15371
|
+
* function instead and calls the node at the end of it - which is the same answer this menu
|
|
15372
|
+
* gives for every other confirmation.
|
|
15373
|
+
*/
|
|
15374
|
+
| {
|
|
15375
|
+
readonly type: "quitApp";
|
|
15376
|
+
}
|
|
15377
|
+
/** Advance the line on screen. */
|
|
15378
|
+
| {
|
|
15379
|
+
readonly type: "next";
|
|
15380
|
+
}
|
|
15381
|
+
/** Auto-forward on/off. Ticked while on. */
|
|
15382
|
+
| {
|
|
15383
|
+
readonly type: "toggleAutoForward";
|
|
15384
|
+
}
|
|
15385
|
+
/** Skipping on/off. Ticked while running. */
|
|
15386
|
+
| {
|
|
15387
|
+
readonly type: "toggleSkipping";
|
|
15388
|
+
}
|
|
15389
|
+
/** Which lines skipping stops at. The two values are a radio pair, never one switch. */
|
|
15390
|
+
| {
|
|
15391
|
+
readonly type: "setSkipReadText";
|
|
15392
|
+
readonly value: boolean;
|
|
15393
|
+
}
|
|
15394
|
+
/** Show or hide the dialogue box. Ticked while shown. */
|
|
15395
|
+
| {
|
|
15396
|
+
readonly type: "toggleDialog";
|
|
15397
|
+
}
|
|
15398
|
+
/** Step back one line of history. Greyed out when there is none. */
|
|
15399
|
+
| {
|
|
15400
|
+
readonly type: "historyUndo";
|
|
15401
|
+
}
|
|
15402
|
+
/** Step forward again after an undo. Greyed out when there is none. */
|
|
15403
|
+
| {
|
|
15404
|
+
readonly type: "historyRedo";
|
|
15405
|
+
}
|
|
15406
|
+
/** Full-screen on/off. Ticked while full-screen, including when the OS did it. */
|
|
15407
|
+
| {
|
|
15408
|
+
readonly type: "toggleFullscreen";
|
|
15409
|
+
}
|
|
15410
|
+
/**
|
|
15411
|
+
* Call a function declared in the global blueprint.
|
|
15412
|
+
*
|
|
15413
|
+
* `fnRef` is the catalogue's own reference (see `fnCatalog`), and `args` are keyed by the head's
|
|
15414
|
+
* parameter pin ids. Everything the named actions above do not cover goes through here -
|
|
15415
|
+
* including every confirmation, because what a game asks before losing a playthrough is the
|
|
15416
|
+
* author's screen and not a native box in a language the game may not even be running in.
|
|
15417
|
+
*/
|
|
15418
|
+
| {
|
|
15419
|
+
readonly type: "fn";
|
|
15420
|
+
readonly fnRef: string;
|
|
15421
|
+
readonly args?: Readonly<Record<string, unknown>>;
|
|
15422
|
+
};
|
|
15423
|
+
type GameMenuItemSpec = {
|
|
15424
|
+
readonly kind: "separator";
|
|
15425
|
+
} | {
|
|
15426
|
+
readonly kind: "submenu";
|
|
15427
|
+
readonly label: GameMenuLabel;
|
|
15428
|
+
readonly items: readonly GameMenuItemSpec[];
|
|
15429
|
+
} | {
|
|
15430
|
+
readonly kind: "action";
|
|
15431
|
+
readonly label: GameMenuLabel;
|
|
15432
|
+
readonly action: GameMenuAction;
|
|
15433
|
+
} | {
|
|
15434
|
+
readonly kind: "dynamic";
|
|
15435
|
+
readonly source: GameMenuDynamicSource;
|
|
15436
|
+
};
|
|
15437
|
+
type GameMenuSpecMenu = {
|
|
15438
|
+
readonly label: GameMenuLabel;
|
|
15439
|
+
readonly items: readonly GameMenuItemSpec[];
|
|
15440
|
+
};
|
|
15441
|
+
type GameMenuSpec = {
|
|
15442
|
+
readonly menus: readonly GameMenuSpecMenu[];
|
|
15443
|
+
};
|
|
15444
|
+
/**
|
|
15445
|
+
* How much memory a running game costs the machine it runs on, process by process.
|
|
15446
|
+
*
|
|
15447
|
+
* Read in the main process from Electron's process metrics (`app.getAppMetrics()`), which is the
|
|
15448
|
+
* operating system's own account: the same working set a task manager shows. Nothing a page can
|
|
15449
|
+
* read gives this. `performance.memory` describes one JavaScript heap, and in Electron it is also
|
|
15450
|
+
* rounded to coarse buckets - the two arms of a measured comparison both read exactly 92.9 MB -
|
|
15451
|
+
* while the memory a visual novel actually spends is mostly outside any heap:
|
|
15452
|
+
* decoded pictures, textures in the GPU process, audio buffers, the main process serving and
|
|
15453
|
+
* decrypting assets. The whole game is several processes, and only the process that owns them can
|
|
15454
|
+
* see all of them.
|
|
15455
|
+
*
|
|
15456
|
+
* `process.memoryUsage()` in the main process was the other candidate and answers a narrower
|
|
15457
|
+
* question - the main process's own heap and buffers - which is the one process a game's pictures
|
|
15458
|
+
* are not in.
|
|
15459
|
+
*
|
|
15460
|
+
* Comments in English per project convention.
|
|
15461
|
+
*/
|
|
15462
|
+
/** What a process is for, in the words a reader of a memory report needs. */
|
|
15463
|
+
type GameProcessKind = "main" | "renderer" | "gpu" | "utility" | "other";
|
|
15464
|
+
type GameProcessMemoryEntry = {
|
|
15465
|
+
kind: GameProcessKind;
|
|
15466
|
+
/**
|
|
15467
|
+
* What the platform calls it, when it says: a utility process names its service ("Network
|
|
15468
|
+
* Service", "Audio Service", a sidecar's own name). Absent for the rest.
|
|
15469
|
+
*/
|
|
15470
|
+
name?: string;
|
|
15471
|
+
/** Whether this is the process the asking page runs in. */
|
|
15472
|
+
current: boolean;
|
|
15473
|
+
/** Memory currently resident in physical RAM, in bytes. */
|
|
15474
|
+
workingSetBytes: number;
|
|
15475
|
+
/** The most this process has ever had resident, in bytes. */
|
|
15476
|
+
peakWorkingSetBytes: number;
|
|
15477
|
+
/** Memory no other process shares, in bytes. Windows reports it; other platforms do not. */
|
|
15478
|
+
privateBytes?: number;
|
|
15479
|
+
};
|
|
15480
|
+
type GameProcessMemoryReading = {
|
|
15481
|
+
/**
|
|
15482
|
+
* What the processes below add up to.
|
|
15483
|
+
*
|
|
15484
|
+
* - `game` - every process the game is made of. A packaged game and a preview.
|
|
15485
|
+
* - `window` - only the process that draws this window. Dev Mode: the window is Studio's, and
|
|
15486
|
+
* the processes around it (Studio's main process, its GPU process, its other windows) are
|
|
15487
|
+
* shared with everything else Studio has open, so counting them would report Studio.
|
|
15488
|
+
*/
|
|
15489
|
+
scope: "game" | "window";
|
|
15490
|
+
processes: GameProcessMemoryEntry[];
|
|
15491
|
+
/** The sum of every process's working set, in bytes. */
|
|
15492
|
+
workingSetBytes: number;
|
|
15493
|
+
/** The sum of every process's private bytes, or null where the platform does not report them. */
|
|
15494
|
+
privateBytes: number | null;
|
|
15495
|
+
};
|
|
15496
|
+
declare const UI_INPUT_POINTER_GESTURES: readonly [
|
|
15497
|
+
"click",
|
|
15498
|
+
"doubleClick",
|
|
15499
|
+
"rightClick",
|
|
15500
|
+
"wheelUp",
|
|
15501
|
+
"wheelDown",
|
|
15502
|
+
"wheelLeft",
|
|
15503
|
+
"wheelRight",
|
|
15504
|
+
"longPress",
|
|
15505
|
+
"middleClick"
|
|
15506
|
+
];
|
|
15507
|
+
type UIInputPointerGesture = (typeof UI_INPUT_POINTER_GESTURES)[number];
|
|
15508
|
+
type UIInputBinding = {
|
|
15509
|
+
kind: "pointer";
|
|
15510
|
+
gesture: UIInputPointerGesture;
|
|
15511
|
+
} | {
|
|
15512
|
+
kind: "key";
|
|
15513
|
+
key: string;
|
|
15514
|
+
} | {
|
|
15515
|
+
kind: "gamepad";
|
|
15516
|
+
button: string;
|
|
15517
|
+
};
|
|
15518
|
+
type UIInputActionDef = {
|
|
15519
|
+
id: string;
|
|
15520
|
+
/** Author-facing name. */
|
|
15521
|
+
name: string;
|
|
15522
|
+
/** The bindings a surface gets unless it overrides them. */
|
|
15523
|
+
bindings: UIInputBinding[];
|
|
15524
|
+
};
|
|
15525
|
+
type UISurfaceActionEnablement = {
|
|
15526
|
+
actionId: string;
|
|
15527
|
+
/** Whether firing this action stops the input here. Default true. */
|
|
15528
|
+
consume?: boolean;
|
|
15529
|
+
};
|
|
15530
|
+
declare const UI_PAGE_ANIMATION_PRESETS: readonly [
|
|
15531
|
+
"none",
|
|
15532
|
+
"fade",
|
|
15533
|
+
"slide",
|
|
15534
|
+
"push",
|
|
15535
|
+
"zoom",
|
|
15536
|
+
"pop",
|
|
15537
|
+
"blur"
|
|
15538
|
+
];
|
|
15539
|
+
declare const UI_PAGE_ANIMATION_DIRECTIONS: readonly [
|
|
15540
|
+
"auto",
|
|
15541
|
+
"left",
|
|
15542
|
+
"right",
|
|
15543
|
+
"up",
|
|
15544
|
+
"down",
|
|
15545
|
+
"angle"
|
|
15546
|
+
];
|
|
15547
|
+
type UIPageAnimationPreset = (typeof UI_PAGE_ANIMATION_PRESETS)[number];
|
|
15548
|
+
type UIPageAnimationDirection = (typeof UI_PAGE_ANIMATION_DIRECTIONS)[number];
|
|
15549
|
+
type UIPageAnimationSettings = {
|
|
15550
|
+
enter: UIPageAnimationPreset;
|
|
15551
|
+
exit: UIPageAnimationPreset;
|
|
15552
|
+
enterDirection: UIPageAnimationDirection;
|
|
15553
|
+
exitDirection: UIPageAnimationDirection;
|
|
15554
|
+
enterAngleDegrees: number;
|
|
15555
|
+
exitAngleDegrees: number;
|
|
15556
|
+
enterDurationSeconds: number;
|
|
15557
|
+
exitDurationSeconds: number;
|
|
15558
|
+
/** Held before this host's own enter starts, counted from the moment its subtree begins entering. */
|
|
15559
|
+
enterDelaySeconds: number;
|
|
15560
|
+
/** Held before this host's own exit starts, counted from the moment its subtree begins exiting. */
|
|
15561
|
+
exitDelaySeconds: number;
|
|
15562
|
+
/** Added to each successive direct child, so a row of children arrives (and leaves) one after another. */
|
|
15563
|
+
childStaggerSeconds: number;
|
|
15564
|
+
/** Hold this host's own exit until every child has finished leaving. Chains through nested parents. */
|
|
15565
|
+
exitWaitsForChildren: boolean;
|
|
15566
|
+
/** Surfaces only: hold the incoming Page until this exit has finished. */
|
|
15567
|
+
exitBlocking: boolean;
|
|
15568
|
+
};
|
|
15569
|
+
type UISurfaceBackgroundFillMode = "cover" | "contain" | "stretch" | "tile";
|
|
15570
|
+
type UISurfaceBackgroundImage = {
|
|
15571
|
+
/** Image library asset id. Absent background = absent field, so this is never empty. */
|
|
15572
|
+
assetId: string;
|
|
15573
|
+
fillMode: UISurfaceBackgroundFillMode;
|
|
15574
|
+
};
|
|
15575
|
+
type UIDocumentVersion = number;
|
|
15576
|
+
type UIDocumentId = string;
|
|
15577
|
+
type UISurfaceId = string;
|
|
15578
|
+
type UIElementId = string;
|
|
15579
|
+
type UIDocument = {
|
|
15580
|
+
schemaVersion: UIDocumentVersion;
|
|
15581
|
+
id: UIDocumentId;
|
|
15582
|
+
name: string;
|
|
15583
|
+
surfaces: UISurface[];
|
|
15584
|
+
components?: UIComponentDefinition[];
|
|
15585
|
+
elements: Record<UIElementId, UIElement>;
|
|
15586
|
+
/**
|
|
15587
|
+
* The shapes this document's widgets describe their data with, keyed by id.
|
|
15588
|
+
*
|
|
15589
|
+
* No authoring surface manages this table: an author edits the fields on the widget that uses
|
|
15590
|
+
* them, and a widget stores only the id. It is a document-level table rather than a copy on each
|
|
15591
|
+
* widget so that a shape has one spelling for the whole project - which is what lets one list's
|
|
15592
|
+
* content feed another's, and what a Break node's pins are generated from. The rules that keep
|
|
15593
|
+
* an implicit library safe to edit are in `struct.ts`.
|
|
15594
|
+
*/
|
|
15595
|
+
structs?: Record<UIStructId, UIStructDef>;
|
|
15596
|
+
/**
|
|
15597
|
+
* What the gestures of this project mean, keyed by id.
|
|
15598
|
+
*
|
|
15599
|
+
* The vocabulary half of the input model: "advance", "skip", "open the menu" - named once for
|
|
15600
|
+
* the whole project, with the bindings a surface gets unless it says otherwise. A surface stores
|
|
15601
|
+
* only the id, in {@link UISurface.actions}, so renaming an action here renames it everywhere
|
|
15602
|
+
* and rebinding it rebinds every surface that took the default. The rules are in
|
|
15603
|
+
* `inputAction.ts`.
|
|
15604
|
+
*/
|
|
15605
|
+
actions?: Record<string, UIInputActionDef>;
|
|
15606
|
+
/**
|
|
15607
|
+
* The page the game starts on, by id.
|
|
15608
|
+
*
|
|
15609
|
+
* Absent means the page carrying `MAIN_APP_SURFACE_ID` - the id a new document gives its first
|
|
15610
|
+
* page, and the only entry a document had before this field existed - so a document whose author
|
|
15611
|
+
* never moved the entry stays exactly as short as it was. Read only through `resolveEntrySurface`
|
|
15612
|
+
* (`entrySurface.ts`), which also says what a pointer that has lost its page falls back to.
|
|
15613
|
+
*
|
|
15614
|
+
* A pointer rather than an id swap: a page's id is what its blueprints are filed under and what
|
|
15615
|
+
* every `Go Page` names, so making another page the entry must not change which id it has.
|
|
15616
|
+
*
|
|
15617
|
+
* Carries no schema bump, for the reason `actions` and `structs` did not: absence already means a
|
|
15618
|
+
* defined answer, so a document written before this field reads correctly without one.
|
|
15619
|
+
*/
|
|
15620
|
+
entrySurfaceId?: UISurfaceId;
|
|
15621
|
+
meta?: UIDocumentMeta;
|
|
15622
|
+
};
|
|
15623
|
+
type UIDocumentMeta = {
|
|
15624
|
+
createdAt?: string;
|
|
15625
|
+
updatedAt?: string;
|
|
15626
|
+
};
|
|
15627
|
+
type UIHost = "app" | "player";
|
|
15628
|
+
type UISurfaceKind = "appSurface" | "stageSurface";
|
|
15629
|
+
type UIStageSlotId = "onStage" | "dialog" | "notification" | "choice" | "nvl";
|
|
15630
|
+
type UIStageSurfaceMount = {
|
|
15631
|
+
kind: "slot";
|
|
15632
|
+
slotId: UIStageSlotId;
|
|
15633
|
+
};
|
|
15634
|
+
type UIAppSurface = {
|
|
15635
|
+
id: UISurfaceId;
|
|
15636
|
+
name: string;
|
|
15637
|
+
host: "app";
|
|
15638
|
+
kind: "appSurface";
|
|
15639
|
+
designSize: UISurfaceDesignSize;
|
|
15640
|
+
rootElementId: UIElementId;
|
|
15641
|
+
settings?: UISurfaceSettings;
|
|
15642
|
+
/**
|
|
15643
|
+
* Which of the project's actions this surface answers. An id here names an entry of
|
|
15644
|
+
* {@link UIDocument.actions}; absent means it answers none of them.
|
|
15645
|
+
*
|
|
15646
|
+
* Input that lands on this surface's content stops here whether or not an action answered it,
|
|
15647
|
+
* unless the action that answered says otherwise. There is no surface-wide setting for that: a
|
|
15648
|
+
* surface drawn over another is drawn over it, and an action is the only thing that knows
|
|
15649
|
+
* whether what it just did leaves anything for the surface behind to do.
|
|
15650
|
+
*/
|
|
15651
|
+
actions?: UISurfaceActionEnablement[];
|
|
15652
|
+
/** The values this page is opened with, in author order. See {@link UIPageParam}. */
|
|
15653
|
+
params?: UIPageParam[];
|
|
15654
|
+
};
|
|
15655
|
+
type UIPageParam = {
|
|
15656
|
+
id: string;
|
|
15657
|
+
name: string;
|
|
15658
|
+
type: UIPageParamType;
|
|
15659
|
+
/**
|
|
15660
|
+
* For a `list`: the shape each row has, as a struct id (an engine shape such as `nl.confirmButton`,
|
|
15661
|
+
* a plugin's, or one of the project's). It types the value on both ends - the inputs that give it
|
|
15662
|
+
* and `Get Page Param` - and a list on the page bound to it is checked against it. Absent for
|
|
15663
|
+
* every other type, and for a list of rows of no declared shape.
|
|
15664
|
+
*/
|
|
15665
|
+
struct?: string;
|
|
15666
|
+
/**
|
|
15667
|
+
* What the page reads when whoever opened it gave nothing for this name, in the declared type.
|
|
15668
|
+
* Absent reads as the type's empty value (`uiPageParamDefaultValue`).
|
|
15669
|
+
*/
|
|
15670
|
+
defaultValue?: unknown;
|
|
15671
|
+
};
|
|
15672
|
+
type UIPageParamType = "string" | "text" | "number" | "boolean" | "list" | "json";
|
|
15673
|
+
type UIStageSurface = {
|
|
15674
|
+
id: UISurfaceId;
|
|
15675
|
+
name: string;
|
|
15676
|
+
host: "player";
|
|
15677
|
+
kind: "stageSurface";
|
|
15678
|
+
designSize: UISurfaceDesignSize;
|
|
15679
|
+
rootElementId: UIElementId;
|
|
15680
|
+
settings?: UISurfaceSettings;
|
|
15681
|
+
mount: UIStageSurfaceMount;
|
|
15682
|
+
slots?: Record<string, UISlotDefinition>;
|
|
15683
|
+
/** @see UIAppSurface.actions */
|
|
15684
|
+
actions?: UISurfaceActionEnablement[];
|
|
15685
|
+
};
|
|
15686
|
+
type UISurface = UIAppSurface | UIStageSurface;
|
|
15687
|
+
type UISurfaceDesignSize = {
|
|
15688
|
+
width: number;
|
|
15689
|
+
height: number;
|
|
15690
|
+
};
|
|
15691
|
+
type UISurfaceSettings = {
|
|
15692
|
+
backgroundColor?: string;
|
|
15693
|
+
/**
|
|
15694
|
+
* Painted over {@link backgroundColor}, inside the design area only - the letterbox bars around
|
|
15695
|
+
* a scaled Surface keep showing the colour, so the two settings are not alternatives.
|
|
15696
|
+
*/
|
|
15697
|
+
backgroundImage?: UISurfaceBackgroundImage;
|
|
15698
|
+
pageAnimation?: UIPageAnimationSettings;
|
|
15699
|
+
/**
|
|
15700
|
+
* What the asset set this Surface's own background names resolves to, per locale. Build-written
|
|
15701
|
+
* exactly as {@link UIElement.assetVariants} is, and here rather than on the Surface so the
|
|
15702
|
+
* answer sits beside the only asset a Surface names by itself.
|
|
15703
|
+
*/
|
|
15704
|
+
assetVariants?: AssetVariantMap;
|
|
15705
|
+
};
|
|
15706
|
+
type UISlotDefinition = {
|
|
15707
|
+
id: string;
|
|
15708
|
+
name: string;
|
|
15709
|
+
rootElementId?: UIElementId;
|
|
15710
|
+
};
|
|
15711
|
+
type UIComponentId = string;
|
|
15712
|
+
type UIComponentDefinition = {
|
|
15713
|
+
id: UIComponentId;
|
|
15714
|
+
name: string;
|
|
15715
|
+
rootElementId: UIElementId;
|
|
15716
|
+
elements: Record<UIElementId, UIElement>;
|
|
15717
|
+
/** Declared in the component's own inspector; each instance supplies its own values. */
|
|
15718
|
+
params?: UIComponentParam[];
|
|
15719
|
+
previewMeta?: {
|
|
15720
|
+
width?: number;
|
|
15721
|
+
height?: number;
|
|
15862
15722
|
};
|
|
15723
|
+
createdAt?: string;
|
|
15724
|
+
updatedAt?: string;
|
|
15725
|
+
};
|
|
15726
|
+
type UIComponentParam = {
|
|
15727
|
+
/** Stable identity. Blueprints and instance values reference this, so renaming `name` is free. */
|
|
15728
|
+
id: string;
|
|
15729
|
+
/** What the author called it, shown in the instance's inspector. */
|
|
15730
|
+
name: string;
|
|
15731
|
+
type: UIComponentParamType;
|
|
15732
|
+
/**
|
|
15733
|
+
* Used by an instance that has not overridden it, and by the definition's own preview. For a
|
|
15734
|
+
* `text` parameter these are words written directly, translated through the definition's own unit
|
|
15735
|
+
* (`uiComponentParamUnitId(componentId, paramId)`).
|
|
15736
|
+
*/
|
|
15737
|
+
defaultValue: string;
|
|
15738
|
+
};
|
|
15739
|
+
type UIComponentParamType = "string" | "text" | "audioTrack";
|
|
15740
|
+
type UIElement = {
|
|
15741
|
+
id: UIElementId;
|
|
15742
|
+
type: string;
|
|
15743
|
+
name?: string;
|
|
15744
|
+
parentId: UIElementId | null;
|
|
15745
|
+
childrenIds: UIElementId[];
|
|
15746
|
+
layout: UILayout;
|
|
15747
|
+
style?: UIStyle;
|
|
15748
|
+
props?: Record<string, unknown>;
|
|
15749
|
+
valueBindings?: Record<string, UIElementValueBinding>;
|
|
15750
|
+
/**
|
|
15751
|
+
* How this element arrives and leaves - the same record a Surface uses for its page animation.
|
|
15752
|
+
* Absent means it does neither. Read it through `elementAnimation.ts` rather than directly.
|
|
15753
|
+
*
|
|
15754
|
+
* Its own field rather than a `props` key, because it belongs to the placement and not to the
|
|
15755
|
+
* widget: every type has one, no renderer reads it, and a linked component instance may set it
|
|
15756
|
+
* even though its props come from the definition.
|
|
15757
|
+
*/
|
|
15758
|
+
animation?: UIPageAnimationSettings;
|
|
15759
|
+
extra?: Record<string, unknown>;
|
|
15760
|
+
/**
|
|
15761
|
+
* What each asset set THIS element names resolves to, per locale.
|
|
15762
|
+
*
|
|
15763
|
+
* **Never authored and never on disk under `editor/`.** It is written while a package is being
|
|
15764
|
+
* assembled (`@shared/build/uiAssetSets`) and exists only in the bundle a game runs from, which
|
|
15765
|
+
* is why editing a widget cannot produce one and why version control never sees it.
|
|
15766
|
+
*
|
|
15767
|
+
* It sits on the element rather than in a document-wide table for the reason the story row's
|
|
15768
|
+
* copy gives: a table would enumerate every localized asset the project has, which is the one
|
|
15769
|
+
* thing a protected pack refuses to disclose. One element discloses the variants of the sets
|
|
15770
|
+
* that element itself uses, which is what drawing the widget already discloses.
|
|
15771
|
+
*
|
|
15772
|
+
* Keyed by the set id the props still name - the prop keeps the set id, so a widget whose map is
|
|
15773
|
+
* missing fails loudly at a resolver rather than quietly fetching one language's picture.
|
|
15774
|
+
*/
|
|
15775
|
+
assetVariants?: AssetVariantMap;
|
|
15863
15776
|
};
|
|
15864
|
-
type
|
|
15865
|
-
type
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15777
|
+
type UIElementValueBindingValueType = "string" | "json" | "float" | "boolean";
|
|
15778
|
+
type UIElementValueBinding = {
|
|
15779
|
+
kind: "blueprintValue";
|
|
15780
|
+
blueprintId: string;
|
|
15781
|
+
valueType: UIElementValueBindingValueType;
|
|
15782
|
+
}
|
|
15783
|
+
/**
|
|
15784
|
+
* Read one declared field of the list item this element is being drawn for.
|
|
15785
|
+
*
|
|
15786
|
+
* The whole reason the struct model exists. Reaching a row's own data used to take a private
|
|
15787
|
+
* blueprint per element - Get List Item Props into Get JSON Field with the key typed into a
|
|
15788
|
+
* string pin - which is three abstractions deep for "show the title". This is the same read with
|
|
15789
|
+
* none of them: the field is picked from a dropdown, and an element outside an item template
|
|
15790
|
+
* simply has nothing to bind to.
|
|
15791
|
+
*
|
|
15792
|
+
* No `valueType` of its own. The field declares one and the bound prop declares one, and the
|
|
15793
|
+
* conversion between them belongs to the target, not to the binding - the same reason the
|
|
15794
|
+
* slider's binding does not restate that a slider holds a number.
|
|
15795
|
+
*/
|
|
15796
|
+
| {
|
|
15797
|
+
kind: "listItemField";
|
|
15798
|
+
fieldId: string;
|
|
15799
|
+
}
|
|
15800
|
+
/**
|
|
15801
|
+
* Show the value one placement gives a text parameter of the component this element is part of.
|
|
15802
|
+
*
|
|
15803
|
+
* The component's counterpart of `listItemField`, and the same kind of read: no graph, nothing
|
|
15804
|
+
* kept alive - the value is taken from the placement being drawn (`resolveUIComponentTextParams`),
|
|
15805
|
+
* so the editing canvas draws each placement with its own words, and the game translates each
|
|
15806
|
+
* through the placement's own unit or reads the key it names. Only a text parameter answers one,
|
|
15807
|
+
* and only a widget's words take one; outside a placement (the component's own editor) the
|
|
15808
|
+
* element's own words are drawn, as sample text.
|
|
15809
|
+
*/
|
|
15810
|
+
| {
|
|
15811
|
+
kind: "componentParam";
|
|
15812
|
+
paramId: string;
|
|
15813
|
+
}
|
|
15814
|
+
/**
|
|
15815
|
+
* Show the value the page this element is on was opened with for one of its text parameters.
|
|
15816
|
+
*
|
|
15817
|
+
* The page's counterpart of `componentParam`, read the same way: no graph - the value is taken from
|
|
15818
|
+
* the props the page was opened with (`resolveUIPageTextParams`), words given at the opening shown
|
|
15819
|
+
* as given, the declared default translated through the page's own unit. Only a text parameter
|
|
15820
|
+
* answers one, only a widget's words take one, and only on a page: a component's insides and a
|
|
15821
|
+
* Game UI have no page parameters to show. The editing canvas, where the page has not been opened,
|
|
15822
|
+
* draws the default, or the element's own words as sample text while the default is empty.
|
|
15823
|
+
*/
|
|
15824
|
+
| {
|
|
15825
|
+
kind: "pageParam";
|
|
15826
|
+
paramId: string;
|
|
15827
|
+
};
|
|
15828
|
+
type UILayout = {
|
|
15829
|
+
x: number;
|
|
15830
|
+
y: number;
|
|
15831
|
+
width: number;
|
|
15832
|
+
height: number;
|
|
15833
|
+
rotation?: number;
|
|
15834
|
+
opacity?: number;
|
|
15835
|
+
visible?: boolean;
|
|
15836
|
+
/** When true, width and height stay proportional in the inspector and on-canvas resize. */
|
|
15837
|
+
lockAspectRatio?: boolean;
|
|
15838
|
+
};
|
|
15839
|
+
type UIStyle = Record<string, unknown>;
|
|
15840
|
+
type GameTestLogLevel = "verbose" | "info" | "success" | "warning" | "error";
|
|
15841
|
+
type GameTestExitReason =
|
|
15842
|
+
/** Closed from inside the game - the window's own close, or the engine quitting itself. */
|
|
15843
|
+
"closed-by-user"
|
|
15844
|
+
/** The host asked: an explicit stop, or the run being cancelled. */
|
|
15845
|
+
| "stopped-by-host"
|
|
15846
|
+
/** Non-zero exit code, a fatal signal, or an uncaught exception in the game's main process. */
|
|
15847
|
+
| "crashed"
|
|
15848
|
+
/** Never got far enough to run: compile failed, or the runner binary would not spawn. */
|
|
15849
|
+
| "failed-to-start";
|
|
15850
|
+
type GameTestExit = {
|
|
15851
|
+
reason: GameTestExitReason;
|
|
15852
|
+
code: number | null;
|
|
15853
|
+
signal: string | null;
|
|
15854
|
+
};
|
|
15855
|
+
type GameTestChoiceOption = {
|
|
15856
|
+
index: number;
|
|
15857
|
+
text: string;
|
|
15858
|
+
/** Offered but not selectable: the option's `disabledWhen` condition holds. */
|
|
15859
|
+
disabled: boolean;
|
|
15860
|
+
};
|
|
15861
|
+
type GameTestEvent = {
|
|
15862
|
+
kind: "console";
|
|
15863
|
+
level: GameTestLogLevel;
|
|
15864
|
+
source: string;
|
|
15865
|
+
message: string;
|
|
15866
|
+
}
|
|
15867
|
+
/**
|
|
15868
|
+
* An uncaught error inside the running game - `window.onerror` / `unhandledrejection` in its
|
|
15869
|
+
* renderer, `uncaughtException` / `unhandledRejection` in its main process. The game runtime had
|
|
15870
|
+
* no such hook before this pipeline; an uncaught renderer exception was reported nowhere.
|
|
15871
|
+
*/
|
|
15872
|
+
| {
|
|
15873
|
+
kind: "runtime-error";
|
|
15874
|
+
scope: "renderer" | "main";
|
|
15875
|
+
message: string;
|
|
15876
|
+
stack?: string;
|
|
15877
|
+
}
|
|
15878
|
+
/** The engine reached an ending (`event:state.end`), forwarded out of the game's renderer. */
|
|
15879
|
+
| {
|
|
15880
|
+
kind: "game-end";
|
|
15881
|
+
}
|
|
15882
|
+
/**
|
|
15883
|
+
* An `/ending` row ran, naming the ending it declares.
|
|
15884
|
+
*
|
|
15885
|
+
* Beside `game-end` rather than replacing it: `game-end` also fires for a story that simply ran
|
|
15886
|
+
* out of rows, which has no ending to name. A test whose pass condition is "*this* ending" can
|
|
15887
|
+
* only be written against this one.
|
|
15888
|
+
*/
|
|
15889
|
+
| {
|
|
15890
|
+
kind: "ending";
|
|
15891
|
+
endingId: string;
|
|
15892
|
+
name: string;
|
|
15893
|
+
}
|
|
15894
|
+
/**
|
|
15895
|
+
* A choice menu is on screen, with every option it evaluated.
|
|
15896
|
+
*
|
|
15897
|
+
* Fires each time a menu mounts, so a rollback into the same choice reports it again. The list
|
|
15898
|
+
* is what `choose` addresses; see {@link GameTestChoiceOption}.
|
|
15899
|
+
*/
|
|
15900
|
+
| {
|
|
15901
|
+
kind: "choice";
|
|
15902
|
+
options: GameTestChoiceOption[];
|
|
15903
|
+
} | {
|
|
15904
|
+
kind: "exit";
|
|
15905
|
+
exit: GameTestExit;
|
|
15906
|
+
};
|
|
15907
|
+
type GameTestCommand =
|
|
15908
|
+
/**
|
|
15909
|
+
* Begin a story at a scene.
|
|
15910
|
+
*
|
|
15911
|
+
* Necessary because a test session boots to the main app surface - the title screen - and no
|
|
15912
|
+
* story runs until something starts one. The caller supplies the target, exactly as the
|
|
15913
|
+
* blueprint behind a Start button does.
|
|
15914
|
+
*/
|
|
15915
|
+
{
|
|
15916
|
+
kind: "start";
|
|
15917
|
+
storyId: string;
|
|
15918
|
+
sceneId: string;
|
|
15919
|
+
}
|
|
15920
|
+
/** One click on the dialogue: finish the line being typed, or move to the next. */
|
|
15921
|
+
| {
|
|
15922
|
+
kind: "advance";
|
|
15923
|
+
}
|
|
15924
|
+
/** Pick an option of the choice currently on screen, by the index the `choice` event reported. */
|
|
15925
|
+
| {
|
|
15926
|
+
kind: "choose";
|
|
15927
|
+
index: number;
|
|
15928
|
+
};
|
|
15929
|
+
type UITemplateSurfacePlacement = {
|
|
15930
|
+
kind: UISurfaceKind;
|
|
15931
|
+
/** Required for `stageSurface`; the game-UI slot the surface mounts into. */
|
|
15932
|
+
slotId?: UIStageSlotId;
|
|
15933
|
+
};
|
|
15934
|
+
interface AssetTransferManifestEntry {
|
|
15935
|
+
/**
|
|
15936
|
+
* The asset's id in the source project, which the importing project reuses.
|
|
15937
|
+
*
|
|
15938
|
+
* Reusing it is what lets the rows that travelled alongside keep pointing at their files
|
|
15939
|
+
* without being rewritten, and it makes a second paste of the same clipboard a no-op: the id
|
|
15940
|
+
* is already in the library, so there is nothing to import.
|
|
15941
|
+
*/
|
|
15942
|
+
assetId: string;
|
|
15943
|
+
/** Author-facing file name, extension included: what the file is called once imported. */
|
|
15944
|
+
fileName: string;
|
|
15945
|
+
/**
|
|
15946
|
+
* The `AssetType` value as a string.
|
|
15947
|
+
*
|
|
15948
|
+
* Structural for the reason `assetSet.ts` gives for the same field: the enum lives under the
|
|
15949
|
+
* renderer and cannot be imported here.
|
|
15950
|
+
*/
|
|
15951
|
+
type: string;
|
|
15952
|
+
/** Size in bytes, when the offering side already knew it. */
|
|
15953
|
+
size?: number;
|
|
15954
|
+
/**
|
|
15955
|
+
* Whether the payload is a directory rather than a single file.
|
|
15956
|
+
*
|
|
15957
|
+
* True for model bundles, the one asset type whose contents are a tree. It decides both halves
|
|
15958
|
+
* of the transfer: the grant covering the entry reaches everything below the path, and the
|
|
15959
|
+
* importing project copies the tree instead of writing bytes.
|
|
15960
|
+
*/
|
|
15961
|
+
isDirectory?: boolean;
|
|
15962
|
+
}
|
|
15869
15963
|
type PrivilegedWriteBatchEntry = {
|
|
15870
15964
|
path: string;
|
|
15871
15965
|
encoding?: FsTextEncoding;
|
|
@@ -16160,6 +16254,19 @@ export type SearchJumpTarget = {
|
|
|
16160
16254
|
unitId?: string;
|
|
16161
16255
|
storyId?: string;
|
|
16162
16256
|
}
|
|
16257
|
+
/**
|
|
16258
|
+
* A translation table showing its orphans: the translations of one language whose line is no
|
|
16259
|
+
* longer in the game (deleted, or disabled), where they are listed and can be deleted.
|
|
16260
|
+
*/
|
|
16261
|
+
| {
|
|
16262
|
+
kind: "translationOrphans";
|
|
16263
|
+
locale: string;
|
|
16264
|
+
}
|
|
16265
|
+
/** A voice table showing its orphans: the takes of one voice language whose line is no longer in the game. */
|
|
16266
|
+
| {
|
|
16267
|
+
kind: "voiceOrphans";
|
|
16268
|
+
locale: string;
|
|
16269
|
+
}
|
|
16163
16270
|
/** A story motion, open in its own editor. */
|
|
16164
16271
|
| {
|
|
16165
16272
|
kind: "storyMotion";
|
|
@@ -17497,7 +17604,14 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
17497
17604
|
updateElementLayouts(layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
17498
17605
|
mergeKey?: string;
|
|
17499
17606
|
}): void;
|
|
17500
|
-
|
|
17607
|
+
/**
|
|
17608
|
+
* `skipHistory` is for bookkeeping writes nobody asked for - an inspector filling in the
|
|
17609
|
+
* appearance keys an element predates, the moment it is selected. Recorded, that left an undo
|
|
17610
|
+
* step behind every first selection, so Ctrl+Z after looking around a page did nothing visible.
|
|
17611
|
+
*/
|
|
17612
|
+
updateElementProps(elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
17613
|
+
skipHistory?: boolean;
|
|
17614
|
+
}): void;
|
|
17501
17615
|
/**
|
|
17502
17616
|
* Every widget that reads its words from `keyName`, turned into one that holds `words` itself.
|
|
17503
17617
|
*
|
|
@@ -17755,11 +17869,16 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
17755
17869
|
* (`resolveWorkspaceUndoScope`). A definition's own stack holds edits *inside* it, and a deleted
|
|
17756
17870
|
* definition has no tab to press Ctrl+Z in.
|
|
17757
17871
|
*
|
|
17872
|
+
* `scopeId` names another stack for the one library operation that is made from inside a page's
|
|
17873
|
+
* editor - {@link createComponentFromElements}, from the canvas or the outline. The same rule
|
|
17874
|
+
* puts that step on the page's stack: Ctrl+Z pressed where the gesture was made has to take it
|
|
17875
|
+
* back, and on the project's stack it instead undid whatever the page's last edit had been.
|
|
17876
|
+
*
|
|
17758
17877
|
* Each step is a command over whole records, not a snapshot of the library: whichever direction
|
|
17759
17878
|
* runs reads the records as they stand at that moment and writes them back exactly, and nothing
|
|
17760
17879
|
* else in the library is touched. Tagged, so a live session drops these steps with the interface
|
|
17761
17880
|
* editors' stacks (`LiveSessionService`): taking back an addition after a session would remove
|
|
17762
|
-
* whatever the room built inside it.
|
|
17881
|
+
* whatever the room built inside it. (A page's stack is cleared whole when a session starts.)
|
|
17763
17882
|
*/
|
|
17764
17883
|
private pushLibraryStep;
|
|
17765
17884
|
/**
|
|
@@ -17992,8 +18111,13 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
17992
18111
|
/**
|
|
17993
18112
|
* A new definition made of copies of elements on a page, with the logic they carry.
|
|
17994
18113
|
*
|
|
17995
|
-
* The page is not changed,
|
|
17996
|
-
*
|
|
18114
|
+
* The page is not changed, but the step that takes this back goes on the page's stack all the
|
|
18115
|
+
* same: it is made from the page's canvas or outline, and that is where Ctrl+Z and the Edit menu
|
|
18116
|
+
* reach (`resolveWorkspaceUndoScope`). On the project's stack - the one {@link createEmptyComponent}
|
|
18117
|
+
* uses, from the rail - Ctrl+Z right after it undid the page's previous edit and left the copy.
|
|
18118
|
+
*
|
|
18119
|
+
* `name` names the definition; without one it takes the element's name, or the catalog's word for
|
|
18120
|
+
* a component when there are several elements or the one has no name.
|
|
17997
18121
|
*/
|
|
17998
18122
|
createComponentFromElements(surfaceId: string, elementIds: string[], name?: string): UIComponentDefinition | null;
|
|
17999
18123
|
/** Recorded in the definition's own undo stack, as a page's name is in the page's. */
|
|
@@ -18088,7 +18212,10 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18088
18212
|
* element on a page has no parameters to show and is left alone.
|
|
18089
18213
|
*/
|
|
18090
18214
|
setElementComponentParamBinding(elementId: string, propPath: string, paramId: string | null): void;
|
|
18091
|
-
|
|
18215
|
+
/** `skipHistory` as {@link updateElementProps} takes it. */
|
|
18216
|
+
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
18217
|
+
skipHistory?: boolean;
|
|
18218
|
+
}): void;
|
|
18092
18219
|
updateComponentElementAnimation(componentId: string, elementId: string, animation: UIPageAnimationSettings | null, options?: {
|
|
18093
18220
|
mergeKey?: string;
|
|
18094
18221
|
}): void;
|
|
@@ -18107,6 +18234,23 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18107
18234
|
* document made of the component alone, as `moveComponentElements` does.
|
|
18108
18235
|
*/
|
|
18109
18236
|
groupComponentElements(componentId: string, elementIds: readonly string[]): string | null;
|
|
18237
|
+
/**
|
|
18238
|
+
* Make an element its definition's root, taking off the container it sat alone in.
|
|
18239
|
+
*
|
|
18240
|
+
* One step in the definition's history, and the one step there that reaches the pages: every
|
|
18241
|
+
* placement is given the box the element took up inside it (`promoteElementToComponentRoot`), and
|
|
18242
|
+
* taking the step back gives the boxes back with the definition.
|
|
18243
|
+
*/
|
|
18244
|
+
promoteComponentElementToRoot(componentId: string, elementId: string): boolean;
|
|
18245
|
+
/**
|
|
18246
|
+
* Put an empty container around a definition's root, as its new root. Returns the container's id.
|
|
18247
|
+
*
|
|
18248
|
+
* The container takes the root's size and draws nothing of its own, so the definition and every
|
|
18249
|
+
* placement look as they did, and the root can now be given siblings.
|
|
18250
|
+
*/
|
|
18251
|
+
wrapComponentRoot(componentId: string): string | null;
|
|
18252
|
+
/** An element as an Edit menu step names it: its own name, or its widget's. */
|
|
18253
|
+
private describeElementForHistory;
|
|
18110
18254
|
createComponentElement(componentId: string, parentId: string, type: string, layoutPatch?: Partial<UILayout>): UIElement | null;
|
|
18111
18255
|
pasteComponentClipboardPayload(componentId: string, targetParentId: string, beforeChildId: string | null, incoming: UIEditorClipboardPayload): {
|
|
18112
18256
|
ok: true;
|
|
@@ -18145,6 +18289,15 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18145
18289
|
private createNotificationStageTemplate;
|
|
18146
18290
|
private createChoiceStageTemplate;
|
|
18147
18291
|
private createNvlStageTemplate;
|
|
18292
|
+
/**
|
|
18293
|
+
* The actions the project's dialogue box reads on with, as far as a new NVL page can follow them.
|
|
18294
|
+
*
|
|
18295
|
+
* Read from the box's graphs the way the engine's own NVL page reads them
|
|
18296
|
+
* (`resolveDialogueAdvanceActionIds`), and kept to actions the project still defines - an
|
|
18297
|
+
* enablement naming a missing one does nothing, and copying it onto a new surface would only
|
|
18298
|
+
* carry the dead entry further.
|
|
18299
|
+
*/
|
|
18300
|
+
private resolveDialogueAdvanceActionsForStarter;
|
|
18148
18301
|
/** Value graph: `Init -> Return Value` fed by `Get List Item Props -> Get JSON Field(propsPath)`. */
|
|
18149
18302
|
private createListItemPropsValueGraph;
|
|
18150
18303
|
/**
|
|
@@ -18159,8 +18312,24 @@ declare class UIDocumentService extends Service<UIDocumentService> implements IU
|
|
|
18159
18312
|
* Panel (`nl.container`) because collection widgets like `nl.nvl.list` do not expose a
|
|
18160
18313
|
* `Mouse Click` head; the panel's own Mouse Click plus Element Click on the interaction layer
|
|
18161
18314
|
* and the list cover every click region.
|
|
18315
|
+
*
|
|
18316
|
+
* The page's wiring when the project has no action its dialogue box reads on with; see
|
|
18317
|
+
* {@link createNvlActionNextGraph} for the one it gets when it has.
|
|
18162
18318
|
*/
|
|
18163
18319
|
private createNvlNextGraph;
|
|
18320
|
+
/**
|
|
18321
|
+
* Event graph for an NVL page in a project whose dialogue box reads on with actions: each of
|
|
18322
|
+
* those actions runs `Next`, and so does a click on the NVL list.
|
|
18323
|
+
*
|
|
18324
|
+
* The actions bring the keys, the gamepad and a click anywhere on the page that is not on a
|
|
18325
|
+
* control - the same presses that read the box on. The list is the exception: it is a control,
|
|
18326
|
+
* and an action stands down over a control (`pointerInputClaimedByControl`), so a click on the
|
|
18327
|
+
* lines would otherwise do nothing. `Element Click` on the list hears exactly that click and no
|
|
18328
|
+
* other, so no press reads on twice and no widget answers a gesture the page answers as well.
|
|
18329
|
+
*
|
|
18330
|
+
* On the surface's blueprint, because only a surface answers an action.
|
|
18331
|
+
*/
|
|
18332
|
+
private createNvlActionNextGraph;
|
|
18164
18333
|
private configureDefaultNotificationBlueprints;
|
|
18165
18334
|
private configureDefaultChoiceBlueprints;
|
|
18166
18335
|
private configureDefaultNvlBlueprints;
|
|
@@ -19465,7 +19634,9 @@ interface IUIDocumentService extends IService {
|
|
|
19465
19634
|
updateElementLayouts(layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
19466
19635
|
mergeKey?: string;
|
|
19467
19636
|
}): void;
|
|
19468
|
-
updateElementProps(elementId: string, propsPatch: Record<string, unknown
|
|
19637
|
+
updateElementProps(elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
19638
|
+
skipHistory?: boolean;
|
|
19639
|
+
}): void;
|
|
19469
19640
|
ensureElementBlueprintValueBinding(elementId: string, propPath: string, input: {
|
|
19470
19641
|
valueType: UIElementValueBindingValueType;
|
|
19471
19642
|
displayName?: string;
|
|
@@ -19505,7 +19676,9 @@ interface IUIDocumentService extends IService {
|
|
|
19505
19676
|
updateComponentElementLayouts(componentId: string, layoutPatches: Record<string, Partial<UILayout>>, options?: {
|
|
19506
19677
|
mergeKey?: string;
|
|
19507
19678
|
}): void;
|
|
19508
|
-
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown
|
|
19679
|
+
updateComponentElementProps(componentId: string, elementId: string, propsPatch: Record<string, unknown>, options?: {
|
|
19680
|
+
skipHistory?: boolean;
|
|
19681
|
+
}): void;
|
|
19509
19682
|
updateComponentElementExtra(componentId: string, elementId: string, extraPatch: Record<string, unknown>): void;
|
|
19510
19683
|
renameComponentElement(componentId: string, elementId: string, name: string): void;
|
|
19511
19684
|
reorderComponentChildren(componentId: string, parentId: string, orderedChildIds: string[]): void;
|