@px-lsp/protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +25 -0
- package/dist/arrays.d.ts +13 -0
- package/dist/arrays.js +19 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +10 -0
- package/dist/descriptorMetadata.d.ts +51 -0
- package/dist/descriptorMetadata.js +98 -0
- package/dist/descriptorMod.d.ts +66 -0
- package/dist/descriptorMod.js +335 -0
- package/dist/errorLogParser.d.ts +33 -0
- package/dist/errorLogParser.js +125 -0
- package/dist/fsWalk.d.ts +20 -0
- package/dist/fsWalk.js +159 -0
- package/dist/locProperties.d.ts +13 -0
- package/dist/locProperties.js +46 -0
- package/dist/locRefs.d.ts +11 -0
- package/dist/locRefs.js +31 -0
- package/dist/modName.d.ts +6 -0
- package/dist/modName.js +53 -0
- package/dist/protocol.d.ts +1462 -0
- package/dist/protocol.js +201 -0
- package/dist/regex.d.ts +13 -0
- package/dist/regex.js +21 -0
- package/dist/suppression.d.ts +52 -0
- package/dist/suppression.js +173 -0
- package/dist/tigerParser.d.ts +28 -0
- package/dist/tigerParser.js +72 -0
- package/dist/translationCore.d.ts +26 -0
- package/dist/translationCore.js +162 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +3 -0
- package/package.json +39 -0
- package/src/arrays.ts +16 -0
- package/src/constants.ts +12 -0
- package/src/descriptorMetadata.ts +101 -0
- package/src/descriptorMod.ts +354 -0
- package/src/errorLogParser.ts +136 -0
- package/src/fsWalk.ts +126 -0
- package/src/locProperties.ts +43 -0
- package/src/locRefs.ts +38 -0
- package/src/modName.ts +18 -0
- package/src/protocol.ts +1459 -0
- package/src/regex.ts +19 -0
- package/src/suppression.ts +178 -0
- package/src/tigerParser.ts +79 -0
- package/src/translationCore.ts +140 -0
- package/src/types.ts +90 -0
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,1459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom LSP protocol between client and server: method names and payload
|
|
3
|
+
* shapes. Everything crossing the process boundary is declared here so both
|
|
4
|
+
* sides compile against one source of truth. docs/PROTOCOL.md documents the
|
|
5
|
+
* contract for non-VSCode clients; treat changes here as API changes.
|
|
6
|
+
*
|
|
7
|
+
* No `vscode` / `vscode-languageserver` imports: plain wire types only.
|
|
8
|
+
*/
|
|
9
|
+
// Referenced only from the {@link IndexStats} doc link below, which ESLint's
|
|
10
|
+
// unused-vars analysis does not see.
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
12
|
+
import type { IndexStats } from "./types";
|
|
13
|
+
|
|
14
|
+
/** Resolved extension settings, computed client-side (path validation, Steam
|
|
15
|
+
* detection fallbacks, workspace-folder default) and pushed to the server. */
|
|
16
|
+
export interface ParadoxSettings {
|
|
17
|
+
/** Game profile id; absent/unknown ids fall back to the server's default
|
|
18
|
+
* game. Detected client-side per workspace (descriptor file, else setting). */
|
|
19
|
+
gameId?: string;
|
|
20
|
+
gamePath: string | null;
|
|
21
|
+
logsPath: string | null;
|
|
22
|
+
modPath: string | null;
|
|
23
|
+
/** Parent/dependency mod roots (load order, base first) indexed as source "parent"
|
|
24
|
+
*, the submod / compatibility-patch workflow. */
|
|
25
|
+
parentPaths: string[];
|
|
26
|
+
/** Workspace mod roots (subset of parentPaths): mods the user is EDITING in
|
|
27
|
+
* this workspace, so they get the mod treatment, reference indexing and
|
|
28
|
+
* reference diagnostics, on top of the parent definition scan. */
|
|
29
|
+
workspaceMods?: string[];
|
|
30
|
+
locLanguage: string;
|
|
31
|
+
/** Show inferred scope after scope-changing block openers (off by default). */
|
|
32
|
+
scopeInlayHints: boolean;
|
|
33
|
+
/** Our diagnostic codes to suppress everywhere. */
|
|
34
|
+
diagnosticsIgnore: string[];
|
|
35
|
+
/** Glob patterns (workspace-relative paths) whose diagnostics are suppressed. */
|
|
36
|
+
diagnosticsIgnorePatterns: string[];
|
|
37
|
+
/** When false (default) mod-only: never diagnose files under the game path. */
|
|
38
|
+
diagnosticsVanilla: boolean;
|
|
39
|
+
/** `px.trace.perf`: wall clock for every request, rescan, index change and
|
|
40
|
+
* scan phase into the output channel. Off by default (perf campaign §A2). */
|
|
41
|
+
tracePerf?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* What the connected client can do beyond plain LSP. Every capability is
|
|
46
|
+
* independent and off by default, so a bare client gets the degraded shape
|
|
47
|
+
* without declaring anything and a rich client opts in to exactly the parts
|
|
48
|
+
* it implements.
|
|
49
|
+
*/
|
|
50
|
+
export interface ParadoxClientCapabilities {
|
|
51
|
+
/**
|
|
52
|
+
* The client renders the sanitized `<span style="color:var(--vscode-*)">`
|
|
53
|
+
* markup in hover markdown (VSCode theme variables). Default false: hover
|
|
54
|
+
* cards are plain markdown, with the same content.
|
|
55
|
+
*/
|
|
56
|
+
hoverHtml?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The command ids the client registers, from {@link clientCommands}. The
|
|
59
|
+
* server emits `command:` links and command-carrying code actions ONLY for
|
|
60
|
+
* ids listed here; for the rest it falls back to plain text or a real
|
|
61
|
+
* WorkspaceEdit. Default: none.
|
|
62
|
+
*/
|
|
63
|
+
commands?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* The client watches the mod tree itself and pushes
|
|
66
|
+
* {@link modFileChangedNotification}. The server then does NOT register its
|
|
67
|
+
* own `workspace/didChangeWatchedFiles` watcher. Default false: the server
|
|
68
|
+
* registers one whenever the client supports dynamic registration.
|
|
69
|
+
*/
|
|
70
|
+
ownFileWatcher?: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** initializationOptions passed at LanguageClient start. All fields optional:
|
|
74
|
+
* the server has fail-soft fallbacks for bare clients. */
|
|
75
|
+
export interface ParadoxInitOptions {
|
|
76
|
+
/** Server-side cache directory (the extension's global storage path). */
|
|
77
|
+
storageDir?: string;
|
|
78
|
+
/** What this client can do; see {@link ParadoxClientCapabilities}. Absent
|
|
79
|
+
* fields default to off (the plain-LSP-client shape). */
|
|
80
|
+
client?: ParadoxClientCapabilities;
|
|
81
|
+
/**
|
|
82
|
+
* @deprecated Send {@link ParadoxInitOptions.client} instead. `true` is an
|
|
83
|
+
* alias for `{ hoverHtml: true, commands: <every id in clientCommands>,
|
|
84
|
+
* ownFileWatcher: true }` (what the VSCode extension declared before the
|
|
85
|
+
* capabilities object existed); false/absent means all-off. Ignored when
|
|
86
|
+
* `client` is present.
|
|
87
|
+
*/
|
|
88
|
+
clientCommands?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Root holding the bundled per-game data directories: the server reads
|
|
91
|
+
* `<dataDir>/<gameId>/wikidocs/` and `<dataDir>/<gameId>/freqs.json`.
|
|
92
|
+
* Normally omitted: the server uses `data/` next to its own bundle. Set it
|
|
93
|
+
* when the data ships apart from the server bundle (an embedder unpacking
|
|
94
|
+
* both separately). Re-resolved against the new `gameId` whenever the game
|
|
95
|
+
* changes, so it stays profile-correct.
|
|
96
|
+
*/
|
|
97
|
+
dataDir?: string;
|
|
98
|
+
/**
|
|
99
|
+
* @deprecated Send {@link ParadoxInitOptions.dataDir} instead. Overrides the
|
|
100
|
+
* wikidocs/ folder ALONE, freqs.json still comes from `dataDir`/the bundle,
|
|
101
|
+
* and, being one fixed folder, it does NOT follow a `gameId` change.
|
|
102
|
+
*/
|
|
103
|
+
wikidocsDir?: string;
|
|
104
|
+
settings?: ParadoxSettings;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---- client command ids ----------------------------------------------------
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Client commands the server references in code actions and hover links (part
|
|
111
|
+
* of the wire contract: a client that implements one must register exactly
|
|
112
|
+
* this id and list it in {@link ParadoxClientCapabilities.commands}). They
|
|
113
|
+
* carry the "px." prefix: these are public extension command ids with shipped
|
|
114
|
+
* default keybindings. The prefix was renamed in the Paradox Toolkit rebrand
|
|
115
|
+
* and no fallback to the old ids is registered.
|
|
116
|
+
*/
|
|
117
|
+
export const clientCommands = {
|
|
118
|
+
editLocalization: "px.editLocalization",
|
|
119
|
+
openLocalizationSideBySide: "px.openLocalizationSideBySide",
|
|
120
|
+
showReferences: "px.showReferences",
|
|
121
|
+
} as const;
|
|
122
|
+
|
|
123
|
+
/** Every id in {@link clientCommands}: what a fully capable client registers. */
|
|
124
|
+
export const allClientCommandIds: string[] = Object.values(clientCommands);
|
|
125
|
+
|
|
126
|
+
// ---- client -> server ------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
/** Notification: settings changed; payload {@link ParadoxSettings}. */
|
|
129
|
+
export const configChangedNotification = "paradox/configChanged";
|
|
130
|
+
|
|
131
|
+
/** Notification: a mod file changed on disk; payload {@link ModFileChangeParams}. */
|
|
132
|
+
export const modFileChangedNotification = "paradox/modFileChanged";
|
|
133
|
+
export interface ModFileChangeParams {
|
|
134
|
+
/** Absolute filesystem path (not a URI). */
|
|
135
|
+
fsPath: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Request: re-parse script_docs logs; payload {@link ReloadDocsParams} -> {@link ReloadDocsResult}. */
|
|
139
|
+
export const reloadDocsRequest = "paradox/reloadDocs";
|
|
140
|
+
export interface ReloadDocsParams {
|
|
141
|
+
force: boolean;
|
|
142
|
+
}
|
|
143
|
+
export interface ReloadDocsResult {
|
|
144
|
+
tokens: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Request: index statistics; no payload -> {@link IndexStats}. */
|
|
148
|
+
export const indexStatsRequest = "paradox/indexStats";
|
|
149
|
+
|
|
150
|
+
/** Request: look up localization entries for a key; {@link LookupLocParams} -> {@link LocEntryInfo}[].
|
|
151
|
+
* Mod entries shadow vanilla ones (the full list is returned, mod first). */
|
|
152
|
+
export const lookupLocRequest = "paradox/lookupLoc";
|
|
153
|
+
export interface LookupLocParams {
|
|
154
|
+
key: string;
|
|
155
|
+
}
|
|
156
|
+
export interface LocEntryInfo {
|
|
157
|
+
file: string;
|
|
158
|
+
/** 0-based. */
|
|
159
|
+
line: number;
|
|
160
|
+
source: "vanilla" | "parent" | "mod";
|
|
161
|
+
value?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---- server -> client ------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/** Notification: data health for the status bar; payload {@link StatusPayload}. */
|
|
167
|
+
export const statusNotification = "paradox/status";
|
|
168
|
+
export interface StatusPayload {
|
|
169
|
+
tokens: number;
|
|
170
|
+
tokensFromScriptDocs: boolean;
|
|
171
|
+
/** True when the script_docs tokens came from the BUNDLED dump snapshot
|
|
172
|
+
* (data/<gameId>/script_docs) rather than the user's own dump. */
|
|
173
|
+
tokensFromBundledDumps?: boolean;
|
|
174
|
+
definitions: number;
|
|
175
|
+
/** True while a (re)scan is running. */
|
|
176
|
+
indexing: boolean;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Notification: the definition index changed (debounced server-side); no payload.
|
|
180
|
+
* Overview views re-query on this signal. */
|
|
181
|
+
export const indexChangedNotification = "paradox/indexChanged";
|
|
182
|
+
|
|
183
|
+
/** Notification: a long-running server phase started or finished; payload
|
|
184
|
+
* {@link ProgressPayload}. The status bar lists what is still loading, so a
|
|
185
|
+
* cold workspace says which step it is on instead of looking idle. No
|
|
186
|
+
* percentages: the phases are coarse and the client only shows their state. */
|
|
187
|
+
export const progressNotification = "paradox/progress";
|
|
188
|
+
export interface ProgressPayload {
|
|
189
|
+
/** Stable phase id, so a "done" can find the "start" it belongs to. */
|
|
190
|
+
phase: string;
|
|
191
|
+
state: "start" | "done";
|
|
192
|
+
/** Human-readable label for the phase, sent with "start". */
|
|
193
|
+
detail?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---- overview suite (Phase 4) ------------------------------------------------
|
|
197
|
+
|
|
198
|
+
/** Shared param for the mod-scoped overview requests: restrict the result to
|
|
199
|
+
* one workspace mod (absolute root path). Absent/null = all workspace mods. */
|
|
200
|
+
export interface ModScopedParams {
|
|
201
|
+
modRoot?: string | null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Request: mod content inventory; {@link ModScopedParams} -> {@link ModOverview}. */
|
|
205
|
+
export const modOverviewRequest = "paradox/modOverview";
|
|
206
|
+
export interface OverviewDef {
|
|
207
|
+
name: string;
|
|
208
|
+
file: string;
|
|
209
|
+
line: number;
|
|
210
|
+
}
|
|
211
|
+
export interface OverviewKind {
|
|
212
|
+
kind: string;
|
|
213
|
+
count: number;
|
|
214
|
+
/** Capped list (first N alphabetically); `count` is the real total. */
|
|
215
|
+
defs: OverviewDef[];
|
|
216
|
+
}
|
|
217
|
+
export interface ModOverview {
|
|
218
|
+
kinds: OverviewKind[];
|
|
219
|
+
totalDefs: number;
|
|
220
|
+
totalRefs: number;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Request: localization coverage; {@link ModScopedParams} -> {@link LocCoverage}[]. */
|
|
224
|
+
export const locCoverageRequest = "paradox/locCoverage";
|
|
225
|
+
export interface LocIssue {
|
|
226
|
+
key: string;
|
|
227
|
+
file?: string;
|
|
228
|
+
/** 0-based. */
|
|
229
|
+
line?: number;
|
|
230
|
+
/** For untranslated: the source-language text. */
|
|
231
|
+
value?: string;
|
|
232
|
+
}
|
|
233
|
+
export interface LocCoverage {
|
|
234
|
+
language: string;
|
|
235
|
+
defined: number;
|
|
236
|
+
/** Referenced by mod script / required by schema but not defined anywhere. */
|
|
237
|
+
missing: LocIssue[];
|
|
238
|
+
/** Defined in the mod but never referenced and not overriding vanilla. */
|
|
239
|
+
orphaned: LocIssue[];
|
|
240
|
+
/** Value identical to the source language (only for non-source languages). */
|
|
241
|
+
untranslated: LocIssue[];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Request: override/conflict map; {@link ModScopedParams} -> {@link OverrideInfo}[]. */
|
|
245
|
+
export const overridesRequest = "paradox/overrides";
|
|
246
|
+
export interface OverrideSite {
|
|
247
|
+
source: "vanilla" | "parent" | "mod";
|
|
248
|
+
/** Display label: the owning mod's descriptor name when known, else `source`. */
|
|
249
|
+
label?: string;
|
|
250
|
+
file: string;
|
|
251
|
+
line: number;
|
|
252
|
+
}
|
|
253
|
+
export interface OverrideInfo {
|
|
254
|
+
name: string;
|
|
255
|
+
kind: string;
|
|
256
|
+
mod: OverrideSite;
|
|
257
|
+
shadowed: OverrideSite[];
|
|
258
|
+
/** Folder rule: script is last-in-wins, GUI is first-in-wins. */
|
|
259
|
+
rule: "LIOS" | "FIOS";
|
|
260
|
+
winner: "mod" | "other";
|
|
261
|
+
note?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Request: full event detail for the graph inspector; {@link EventDetailParams} -> {@link EventDetail} | null. */
|
|
265
|
+
export const eventDetailRequest = "paradox/eventDetail";
|
|
266
|
+
export interface EventDetailParams {
|
|
267
|
+
id: string;
|
|
268
|
+
}
|
|
269
|
+
/** A localizable field: key, resolved text, and (for mod entries) the editable site. */
|
|
270
|
+
export interface EventLocField {
|
|
271
|
+
key: string;
|
|
272
|
+
text?: string;
|
|
273
|
+
/** Present only when the entry lives in the mod (in-place editable). */
|
|
274
|
+
file?: string;
|
|
275
|
+
line?: number;
|
|
276
|
+
/** The value comes from a dynamic block (first_valid / triggered_desc), not a plain key. */
|
|
277
|
+
dynamic?: boolean;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* One flattened line of a rendered block: enough to print an event's logic
|
|
281
|
+
* back as readable pseudo-script without the client re-parsing anything.
|
|
282
|
+
*/
|
|
283
|
+
export interface EventScriptLine {
|
|
284
|
+
/** Nesting depth inside the rendered block (0 = a direct child). */
|
|
285
|
+
depth: number;
|
|
286
|
+
/** The statement without indentation: `key = value`, `key = {`, `}`, or a bare scalar. */
|
|
287
|
+
text: string;
|
|
288
|
+
/** 0-based source line. */
|
|
289
|
+
line: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A reference inside a block that hands control to another event or on_action:
|
|
294
|
+
* the step-into edge of an event walkthrough. Collected from the schema's
|
|
295
|
+
* event/on_action reference fields (`trigger_event`, `on_action`, `events`,
|
|
296
|
+
* `random_events`, …), so a game profile that names them differently is
|
|
297
|
+
* covered without a hard-coded key list.
|
|
298
|
+
*/
|
|
299
|
+
export interface EventStepTarget {
|
|
300
|
+
/** The key that produced the reference (`trigger_event`, `on_action`, …). */
|
|
301
|
+
via: string;
|
|
302
|
+
/** Referenced event id / on_action name, exactly as written. */
|
|
303
|
+
name: string;
|
|
304
|
+
/** What the index says `name` is. "unknown" = not indexed; say so, do not guess. */
|
|
305
|
+
kind: "event" | "on_action" | "unknown";
|
|
306
|
+
/** 0-based line of the reference, in the file that contains it (for a
|
|
307
|
+
* {@link EventStepTarget.fires} entry that is the on_action's own file). */
|
|
308
|
+
line: number;
|
|
309
|
+
/** Definition site, when the name is indexed. */
|
|
310
|
+
file?: string;
|
|
311
|
+
defLine?: number;
|
|
312
|
+
/** Definition sites of that kind, when more than one. on_actions merge
|
|
313
|
+
* across files (a mod extending a vanilla on_action), so `fires` reflects
|
|
314
|
+
* only the site at {@link EventStepTarget.file}. */
|
|
315
|
+
defCount?: number;
|
|
316
|
+
/**
|
|
317
|
+
* on_action targets only: what that on_action itself fires, read from its own
|
|
318
|
+
* definition. Empty when the definition names nothing. Absent when there was
|
|
319
|
+
* nothing to read: the name is not an indexed on_action, its file could not
|
|
320
|
+
* be parsed, or this target already IS one level deep (resolution stops
|
|
321
|
+
* there, so a self-chaining pair cannot recurse).
|
|
322
|
+
*/
|
|
323
|
+
fires?: EventStepTarget[];
|
|
324
|
+
/** Real target count before `fires` was capped. */
|
|
325
|
+
firesTotal?: number;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* A scalar `key = value` written directly in an event body or an option body,
|
|
330
|
+
* with the line it sits on, so an editor can rewrite it in place instead of
|
|
331
|
+
* re-parsing the file. Blocks are not fields: they are `sections` / `options`.
|
|
332
|
+
*/
|
|
333
|
+
export interface EventFieldInfo {
|
|
334
|
+
key: string;
|
|
335
|
+
value: string;
|
|
336
|
+
/** 0-based source line. */
|
|
337
|
+
line: number;
|
|
338
|
+
/** The value was written in quotes and must be rewritten that way. */
|
|
339
|
+
quoted?: boolean;
|
|
340
|
+
}
|
|
341
|
+
export interface EventSectionInfo {
|
|
342
|
+
name: string;
|
|
343
|
+
/** 0-based line of the section key. */
|
|
344
|
+
line: number;
|
|
345
|
+
/** Top-level keys inside the section (capped). */
|
|
346
|
+
keys: string[];
|
|
347
|
+
/** The section rendered as pseudo-script, capped (`totalLines` is the truth). */
|
|
348
|
+
lines: EventScriptLine[];
|
|
349
|
+
totalLines: number;
|
|
350
|
+
/** Events / on_actions this section hands control to, capped. */
|
|
351
|
+
targets: EventStepTarget[];
|
|
352
|
+
/** Real target count before `targets` was capped. */
|
|
353
|
+
targetsTotal: number;
|
|
354
|
+
}
|
|
355
|
+
/** A gate block (trigger / ai_chance) rendered for in-place editing. */
|
|
356
|
+
export interface EventGateInfo {
|
|
357
|
+
/** 0-based line of the block's key. */
|
|
358
|
+
line: number;
|
|
359
|
+
lines: EventScriptLine[];
|
|
360
|
+
totalLines: number;
|
|
361
|
+
}
|
|
362
|
+
export interface EventOptionInfo {
|
|
363
|
+
line: number;
|
|
364
|
+
/** Line the option's first statement may be inserted before (0-based). */
|
|
365
|
+
bodyLine: number;
|
|
366
|
+
/** Scalar keys written in the option body, editable in place. */
|
|
367
|
+
fields: EventFieldInfo[];
|
|
368
|
+
name?: EventLocField;
|
|
369
|
+
effectKeys: string[];
|
|
370
|
+
hasTrigger: boolean;
|
|
371
|
+
hasAiChance: boolean;
|
|
372
|
+
/** The option's own trigger block, rendered, when it has one. */
|
|
373
|
+
trigger?: EventGateInfo;
|
|
374
|
+
/** The option's ai_chance block, rendered, when it has one. */
|
|
375
|
+
aiChance?: EventGateInfo;
|
|
376
|
+
/** The option's effects rendered as pseudo-script (name/trigger/ai_chance/
|
|
377
|
+
* ai_value dropped: they gate the option, they are not its effect), capped. */
|
|
378
|
+
lines: EventScriptLine[];
|
|
379
|
+
totalLines: number;
|
|
380
|
+
/** Events / on_actions this option hands control to, capped. */
|
|
381
|
+
targets: EventStepTarget[];
|
|
382
|
+
/** Real target count before `targets` was capped. */
|
|
383
|
+
targetsTotal: number;
|
|
384
|
+
}
|
|
385
|
+
export interface EventRefInfo {
|
|
386
|
+
name: string;
|
|
387
|
+
kind: "saved_scope" | "variable" | "scripted_effect" | "scripted_trigger" | "script_value" | "event";
|
|
388
|
+
/** First use inside the event, 0-based. */
|
|
389
|
+
line: number;
|
|
390
|
+
defFile?: string;
|
|
391
|
+
defLine?: number;
|
|
392
|
+
/** Number of definition/save sites. */
|
|
393
|
+
defCount?: number;
|
|
394
|
+
}
|
|
395
|
+
export interface EventDetail {
|
|
396
|
+
id: string;
|
|
397
|
+
file: string;
|
|
398
|
+
line: number;
|
|
399
|
+
/** Line of the event's closing brace (option-scaffold insertion point). */
|
|
400
|
+
endLine: number;
|
|
401
|
+
/** Line a new top-level statement may be inserted before (0-based). */
|
|
402
|
+
bodyLine: number;
|
|
403
|
+
/** Scalar keys written at the event's top level, editable in place. */
|
|
404
|
+
fields: EventFieldInfo[];
|
|
405
|
+
type?: string;
|
|
406
|
+
hidden?: boolean;
|
|
407
|
+
theme?: string;
|
|
408
|
+
title?: EventLocField;
|
|
409
|
+
desc?: EventLocField;
|
|
410
|
+
/** The event's third displayed string in the games whose events have one
|
|
411
|
+
* (top-level `flavor`); absent everywhere else. */
|
|
412
|
+
flavor?: EventLocField;
|
|
413
|
+
sections: EventSectionInfo[];
|
|
414
|
+
options: EventOptionInfo[];
|
|
415
|
+
refs: EventRefInfo[];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Request: GUI widget tree for a .gui document; {@link GuiTreeParams} -> {@link GuiTree}. */
|
|
419
|
+
export const guiTreeRequest = "paradox/guiTree";
|
|
420
|
+
export interface GuiTreeParams {
|
|
421
|
+
/** For display only; the text is authoritative. */
|
|
422
|
+
uri: string;
|
|
423
|
+
text: string;
|
|
424
|
+
}
|
|
425
|
+
export interface GuiTreeNode {
|
|
426
|
+
/** Widget type or declaration header (window, flowcontainer, "template NAME"…). */
|
|
427
|
+
key: string;
|
|
428
|
+
/** name = "..." when present. */
|
|
429
|
+
name?: string;
|
|
430
|
+
/** For `type x = base { }` / tagged blocks: the base widget type. */
|
|
431
|
+
base?: string;
|
|
432
|
+
/** using = template references. */
|
|
433
|
+
using?: string[];
|
|
434
|
+
/** decl = template/types/type/blockoverride/block headers; state = animation states. */
|
|
435
|
+
kind: "widget" | "state" | "decl";
|
|
436
|
+
/** 0-based line of the key. */
|
|
437
|
+
line: number;
|
|
438
|
+
children: GuiTreeNode[];
|
|
439
|
+
}
|
|
440
|
+
export interface GuiTree {
|
|
441
|
+
nodes: GuiTreeNode[];
|
|
442
|
+
/** Total node count across all depths. */
|
|
443
|
+
count: number;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Request: rendered GUI layout for a .gui document;
|
|
448
|
+
* {@link GuiLayoutParams} -> {@link GuiLayoutResult}. Rectangles come from
|
|
449
|
+
* the measured layout engine (docs/gui-designer/calibration/spec.md), with
|
|
450
|
+
* templates/types resolved against the vanilla + mod gui tree.
|
|
451
|
+
*/
|
|
452
|
+
export const guiLayoutRequest = "paradox/guiLayout";
|
|
453
|
+
export interface GuiLayoutParams {
|
|
454
|
+
/** For display only; the text is authoritative. */
|
|
455
|
+
uri: string;
|
|
456
|
+
text: string;
|
|
457
|
+
/** Conditional-visibility preview mode; absent = `showAll`. */
|
|
458
|
+
visibility?: GuiVisibilityOptions;
|
|
459
|
+
/**
|
|
460
|
+
* `resolve` (default): textbox keys show their localized value and
|
|
461
|
+
* `[datafunctions]` their knowable text, and sizes follow. `raw`: the
|
|
462
|
+
* `text =` value verbatim, as the file has it.
|
|
463
|
+
*/
|
|
464
|
+
loc?: "resolve" | "raw";
|
|
465
|
+
/** Modder-supplied preview text per `[...]` expression (the `.<game>modding/gui-preview-values.json` table). */
|
|
466
|
+
previewValues?: Record<string, string>;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* How the layout treats a CONDITIONALLY visible widget, one whose `visible`
|
|
471
|
+
* holds an expression a static preview cannot evaluate (`visible =
|
|
472
|
+
* "[GetPlayer.IsAI]"`). A literal `visible = no` is deterministic and always
|
|
473
|
+
* collapses; `visible = yes` always shows. Neither is a check.
|
|
474
|
+
*/
|
|
475
|
+
export type GuiVisibilityMode = "showAll" | "hideAll" | "evaluate";
|
|
476
|
+
export interface GuiVisibilityOptions {
|
|
477
|
+
mode: GuiVisibilityMode;
|
|
478
|
+
/**
|
|
479
|
+
* `evaluate` only: per-check assignments. The KEY is the `visible` value
|
|
480
|
+
* exactly as authored, minus its quotes (`[GetPlayer.IsAI]`). The source
|
|
481
|
+
* string is the only identity a static preview has, so two widgets written
|
|
482
|
+
* with the same condition share one toggle, and a key stays stable across
|
|
483
|
+
* edits that do not touch the condition. A check with no assignment behaves
|
|
484
|
+
* as `showAll` (shown).
|
|
485
|
+
*/
|
|
486
|
+
checks?: Record<string, boolean>;
|
|
487
|
+
}
|
|
488
|
+
/** A conditional `visible` the layout met, for building a toggle UI. */
|
|
489
|
+
export interface GuiVisibilityCheck {
|
|
490
|
+
/** The condition source string, the key {@link GuiVisibilityOptions.checks} takes. */
|
|
491
|
+
key: string;
|
|
492
|
+
/** Widgets carrying this condition in this document. */
|
|
493
|
+
count: number;
|
|
494
|
+
/** True when THIS run resolved the check to hidden. */
|
|
495
|
+
hidden: boolean;
|
|
496
|
+
}
|
|
497
|
+
/** Server-side wall clock of one `paradox/guiLayout`, for a stats line. */
|
|
498
|
+
export interface GuiLayoutTimings {
|
|
499
|
+
/** Parsing the document and collecting its own template/type declarations. */
|
|
500
|
+
parseMs: number;
|
|
501
|
+
/** Building the cross-file template/type store; 0 on a cache hit. */
|
|
502
|
+
defsMs: number;
|
|
503
|
+
/** Building the widget tree and arranging every rect. */
|
|
504
|
+
layoutMs: number;
|
|
505
|
+
/** The whole request, server side. */
|
|
506
|
+
totalMs: number;
|
|
507
|
+
}
|
|
508
|
+
export interface GuiLayoutFill {
|
|
509
|
+
texture?: string;
|
|
510
|
+
/** rgba 0..1, straight sRGB multiply (rendered = round(v*255)). */
|
|
511
|
+
color?: [number, number, number, number];
|
|
512
|
+
/**
|
|
513
|
+
* Nine-slice border widths [left, top, right, bottom] in texture pixels
|
|
514
|
+
* (from `spriteborder`/`spriteborder_<side>`). The values as authored;
|
|
515
|
+
* `mode` says whether they apply.
|
|
516
|
+
*/
|
|
517
|
+
border?: [number, number, number, number];
|
|
518
|
+
/**
|
|
519
|
+
* How to draw the texture. Nine-slicing needs BOTH a `Cornered*` spriteType
|
|
520
|
+
* AND a non-zero border; a border alone is ignored and the whole texture
|
|
521
|
+
* stretches. `nineslice-*` = corners unscaled, edges and centre tiled or
|
|
522
|
+
* stretched per the suffix; `tile` = repeat the whole texture.
|
|
523
|
+
*/
|
|
524
|
+
mode?: "stretch" | "tile" | "nineslice-stretch" | "nineslice-tile";
|
|
525
|
+
/** `framesize = { w h }` cell size when the texture is a frame sheet. */
|
|
526
|
+
framesize?: [number, number];
|
|
527
|
+
/** `alpha = x`: the fill's opacity, 0..1 (absent = 1). */
|
|
528
|
+
alpha?: number;
|
|
529
|
+
/**
|
|
530
|
+
* `modify_texture` with `blend_mode = alphamultiply`: a texture whose alpha
|
|
531
|
+
* multiplies the fill's, stretched over the rect. Listed in `textures` like
|
|
532
|
+
* any other path. Other blend modes are not carried.
|
|
533
|
+
*/
|
|
534
|
+
mask?: string;
|
|
535
|
+
/** `fittype = centercrop`: cover the rect and crop to the centre instead of stretching. */
|
|
536
|
+
fit?: "centercrop";
|
|
537
|
+
/**
|
|
538
|
+
* 1-based frame index into that sheet, row-major over the cols x rows grid
|
|
539
|
+
* (cols = texW/w). Out-of-range values clamp to the first or last cell.
|
|
540
|
+
*/
|
|
541
|
+
frame?: number;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* One piece of what a textbox shows. `loc`: a localization key the index
|
|
545
|
+
* resolved (or not: `resolved` false shows the key itself). `datafn`: a
|
|
546
|
+
* `[...]` expression; resolved through `Localize`/`Concept` or the modder's
|
|
547
|
+
* preview values, else shown as its last chain segment with `resolved` false.
|
|
548
|
+
* `source` is the key or the expression without brackets.
|
|
549
|
+
*/
|
|
550
|
+
export interface GuiTextSegment {
|
|
551
|
+
text: string;
|
|
552
|
+
kind: "literal" | "loc" | "datafn";
|
|
553
|
+
source: string;
|
|
554
|
+
resolved: boolean;
|
|
555
|
+
}
|
|
556
|
+
export interface GuiLayoutText {
|
|
557
|
+
/** What is measured and drawn (resolved when the request asked for it). */
|
|
558
|
+
text: string;
|
|
559
|
+
/** The raw `text =` value; differs from `text` when something resolved. */
|
|
560
|
+
raw?: string;
|
|
561
|
+
segments?: GuiTextSegment[];
|
|
562
|
+
fontsize: number;
|
|
563
|
+
offsetX: number;
|
|
564
|
+
offsetY: number;
|
|
565
|
+
lines: string[];
|
|
566
|
+
color?: [number, number, number, number];
|
|
567
|
+
}
|
|
568
|
+
export interface GuiLayoutNode {
|
|
569
|
+
key: string;
|
|
570
|
+
name?: string;
|
|
571
|
+
rect: { x: number; y: number; w: number; h: number };
|
|
572
|
+
/** Scrollarea viewport: children are clipped to the rect. */
|
|
573
|
+
clip: boolean;
|
|
574
|
+
bg?: GuiLayoutFill;
|
|
575
|
+
fill?: GuiLayoutFill;
|
|
576
|
+
text?: GuiLayoutText;
|
|
577
|
+
/** 0-based line of the instance statement in the requested document. */
|
|
578
|
+
line?: number;
|
|
579
|
+
/** Placed via anchor+position rules (position honored -> draggable). */
|
|
580
|
+
positioned: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* `line` is the widget's own statement in this document (safe to edit);
|
|
583
|
+
* false for children spliced from type definitions.
|
|
584
|
+
*/
|
|
585
|
+
editable: boolean;
|
|
586
|
+
/** Raw `position = { x y }` source values, when present. */
|
|
587
|
+
srcPosition?: [number, number];
|
|
588
|
+
/** Raw `size = { w h }` source values, when present. */
|
|
589
|
+
srcSize?: [number, number];
|
|
590
|
+
/**
|
|
591
|
+
* The widget's index among its parent body's REORDER SIBLINGS: exactly the
|
|
592
|
+
* index a `reorder`, `insert` or `delete` op counts (see {@link GuiSourceOp}).
|
|
593
|
+
* Those are the body's DECLARATIONS, which include the `blockoverride` /
|
|
594
|
+
* `block` / `template` entries a preview never shows, so a client that ranks
|
|
595
|
+
* the widgets it can see is off by one per intervening declaration.
|
|
596
|
+
*
|
|
597
|
+
* Absent whenever no index names the node: a template- or type-spliced child,
|
|
598
|
+
* a datamodel ghost, the contents of a named slot, and a scrollarea's
|
|
599
|
+
* pass-through children, whose ranks count a body their drawn parent does not
|
|
600
|
+
* own. Absent means "not addressable by index"; do not fall back to counting.
|
|
601
|
+
*/
|
|
602
|
+
srcIndex?: number;
|
|
603
|
+
/**
|
|
604
|
+
* Placeholder copy of a datamodel item template (the list has no runtime
|
|
605
|
+
* rows in a static preview). The renderer draws it at reduced opacity; it is
|
|
606
|
+
* never editable. Presentation only, not a measured layout rule.
|
|
607
|
+
*/
|
|
608
|
+
ghost?: boolean;
|
|
609
|
+
/**
|
|
610
|
+
* The widget's `onclick` value as authored, minus its quotes, when it has
|
|
611
|
+
* one. A static preview cannot run it; a client's interact mode reads the
|
|
612
|
+
* `GetVariableSystem.*` calls out of it to drive the visibility checks, and
|
|
613
|
+
* names the rest as what the game would run.
|
|
614
|
+
*/
|
|
615
|
+
onclick?: string;
|
|
616
|
+
/** The widget's `tooltip` value as authored (a loc key or a [datafunction]), when it has one. */
|
|
617
|
+
tooltip?: string;
|
|
618
|
+
/**
|
|
619
|
+
* A root that is a `type name = base { }` DECLARATION laid out as one
|
|
620
|
+
* instance of itself. Set only on roots, and only for a document that
|
|
621
|
+
* instantiates nothing at top level, the shape whole panels are written in
|
|
622
|
+
* by the games whose engine instantiates a window by name from code. The
|
|
623
|
+
* declaration header is not editable; the children under it are ordinary
|
|
624
|
+
* statements of the document and are.
|
|
625
|
+
*/
|
|
626
|
+
declared?: boolean;
|
|
627
|
+
children: GuiLayoutNode[];
|
|
628
|
+
}
|
|
629
|
+
export interface GuiLayoutResult {
|
|
630
|
+
nodes: GuiLayoutNode[];
|
|
631
|
+
/** Distinct texture paths referenced anywhere in the tree (mod-relative). */
|
|
632
|
+
textures: string[];
|
|
633
|
+
/** Total node count across all depths. */
|
|
634
|
+
nodeCount: number;
|
|
635
|
+
/** How many .gui files fed the template/type store (0 = no game path). */
|
|
636
|
+
defsFiles: number;
|
|
637
|
+
/**
|
|
638
|
+
* Every conditional `visible` the layout met, key-sorted. Reported in ALL
|
|
639
|
+
* modes, `showAll` included, so a client can build the toggle UI before the
|
|
640
|
+
* user has switched mode.
|
|
641
|
+
*/
|
|
642
|
+
visibilityChecks: GuiVisibilityCheck[];
|
|
643
|
+
/** Per-stage wall clock of this request. */
|
|
644
|
+
timings: GuiLayoutTimings;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Request: the properties of ONE widget, as the layout engine resolved them;
|
|
649
|
+
* {@link GuiWidgetInfoParams} -> {@link GuiWidgetInfo}, null when the line
|
|
650
|
+
* carries no widget of its own (a node spliced in from a template or a type has
|
|
651
|
+
* no source here, the same answer `guiSourceEdit` refuses with).
|
|
652
|
+
*
|
|
653
|
+
* This is the designer inspector's READ side. It is a separate request rather
|
|
654
|
+
* than a field on {@link GuiLayoutNode} because it is per-SELECTION data: a
|
|
655
|
+
* vanilla window lays out 500+ widgets and carrying every widget's expanded
|
|
656
|
+
* property list on every layout push would multiply the payload for rows one
|
|
657
|
+
* widget at a time is ever shown.
|
|
658
|
+
*/
|
|
659
|
+
export const guiWidgetInfoRequest = "paradox/guiWidgetInfo";
|
|
660
|
+
export interface GuiWidgetInfoParams {
|
|
661
|
+
/** For display only; the text is authoritative. */
|
|
662
|
+
uri: string;
|
|
663
|
+
text: string;
|
|
664
|
+
/** 0-based line of the widget's own statement (`GuiLayoutNode.line`). */
|
|
665
|
+
line: number;
|
|
666
|
+
/**
|
|
667
|
+
* Also answer "why is it here": run the layout with an explanation trace on
|
|
668
|
+
* and return {@link GuiWidgetInfo.placement}. Off by default because it costs
|
|
669
|
+
* a full layout of the document; the trace itself is what the flag gates, so
|
|
670
|
+
* an ordinary `paradox/guiLayout` never pays for it.
|
|
671
|
+
*/
|
|
672
|
+
placement?: boolean;
|
|
673
|
+
}
|
|
674
|
+
/** One step of the chain a property was spliced through. */
|
|
675
|
+
export interface GuiWidgetOrigin {
|
|
676
|
+
kind: "type" | "template";
|
|
677
|
+
/** The type or template name, as `expandWidget` resolved it. */
|
|
678
|
+
name: string;
|
|
679
|
+
}
|
|
680
|
+
export interface GuiWidgetProperty {
|
|
681
|
+
key: string;
|
|
682
|
+
/**
|
|
683
|
+
* The value as authored, rendered from the tokens: a quoted scalar keeps its
|
|
684
|
+
* quotes, a block reads `{ a b }`. Blocks come from other files whose text
|
|
685
|
+
* the store does not keep, so this is a rendering, not a byte copy.
|
|
686
|
+
*/
|
|
687
|
+
value: string;
|
|
688
|
+
/**
|
|
689
|
+
* Definitions the entry was spliced through, INNERMOST first (`[template
|
|
690
|
+
* PxDeco, type px_card]` = a template used inside a type). Empty means the
|
|
691
|
+
* property is authored in the widget's own body, which is the only case
|
|
692
|
+
* `setProperties` rewrites in place.
|
|
693
|
+
*/
|
|
694
|
+
origin: GuiWidgetOrigin[];
|
|
695
|
+
/**
|
|
696
|
+
* The values this key SHADOWED, in expansion order (base-most first), so the
|
|
697
|
+
* last entry is the one this row directly overrides. Present only when the
|
|
698
|
+
* key was assigned more than once, which is exactly when the inspector can
|
|
699
|
+
* say "this overrides `{ 100 50 }` from type px_card". Absent otherwise.
|
|
700
|
+
*/
|
|
701
|
+
overrides?: GuiWidgetOverride[];
|
|
702
|
+
}
|
|
703
|
+
/** A value a later assignment of the same key replaced. */
|
|
704
|
+
export interface GuiWidgetOverride {
|
|
705
|
+
/** Rendered the same way {@link GuiWidgetProperty.value} is. */
|
|
706
|
+
value: string;
|
|
707
|
+
/** Where the replaced value came from; empty = the widget's own body. */
|
|
708
|
+
origin: GuiWidgetOrigin[];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* One contribution to a widget's final origin, in engine order. The `dx`/`dy`
|
|
713
|
+
* of the terms sum to the rect's `x`/`y` exactly (see spec.md B1-B/C/D:
|
|
714
|
+
* `x = parent.x + parentanchor.fx*parent.w - widgetanchor.fx*w + position.x`).
|
|
715
|
+
*/
|
|
716
|
+
export interface GuiPlacementTerm {
|
|
717
|
+
kind: "parentOrigin" | "parentanchor" | "widgetanchor" | "position";
|
|
718
|
+
/**
|
|
719
|
+
* The authored spec behind the term (`bottom|right`, `{ -30 -30 }`). Absent
|
|
720
|
+
* on `parentOrigin`, which is the parent's rect rather than a property, and
|
|
721
|
+
* on a `widgetanchor` that was never written (it mirrors `parentanchor`,
|
|
722
|
+
* B1-B/C), there `source` names the anchor it mirrored.
|
|
723
|
+
*/
|
|
724
|
+
source?: string;
|
|
725
|
+
dx: number;
|
|
726
|
+
dy: number;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* The layout container that assigned a rect outright. The engine DROPS an
|
|
731
|
+
* authored `position` on such a child and logs "Widget cannot have a position
|
|
732
|
+
* in a layout" (probe 2026-08-02, parity-checklist L23), which is the single
|
|
733
|
+
* most common "why is my widget not where I put it".
|
|
734
|
+
*/
|
|
735
|
+
export interface GuiPlacedBy {
|
|
736
|
+
/** The parent's widget key (`hbox`, `flowcontainer`, `fixedgridbox`, …). */
|
|
737
|
+
key: string;
|
|
738
|
+
name?: string;
|
|
739
|
+
layout: "box" | "flow" | "grid";
|
|
740
|
+
/** The `position` the engine dropped, when the widget authored one. */
|
|
741
|
+
droppedPosition?: [number, number];
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** Why a widget's rect is where it is. */
|
|
745
|
+
export interface GuiPlacement {
|
|
746
|
+
/** The final rect, the same one `GuiLayoutNode.rect` carries. */
|
|
747
|
+
rect: { x: number; y: number; w: number; h: number };
|
|
748
|
+
/** What the terms are measured against: the parent's content rect, or the
|
|
749
|
+
* viewport for a root widget. */
|
|
750
|
+
parentRect: { x: number; y: number; w: number; h: number };
|
|
751
|
+
/**
|
|
752
|
+
* The anchor terms, summing to the rect origin. EMPTY when `placedBy` is
|
|
753
|
+
* set: a layout container computes the slot, so there is no anchor sum to
|
|
754
|
+
* show.
|
|
755
|
+
*/
|
|
756
|
+
terms: GuiPlacementTerm[];
|
|
757
|
+
placedBy?: GuiPlacedBy;
|
|
758
|
+
/**
|
|
759
|
+
* The innermost clipping ancestor (a scrollarea viewport, or any widget with
|
|
760
|
+
* `scissor = yes`), when one clips this widget. The rect is the clip rect,
|
|
761
|
+
* NOT the intersection: the geometry is true and the renderer clips.
|
|
762
|
+
*/
|
|
763
|
+
clippedBy?: { key: string; name?: string; rect: { x: number; y: number; w: number; h: number } };
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* A texture the widget draws, with its frame-sheet grid when it is one. The
|
|
768
|
+
* sheet's pixel size comes from the DDS header alone (128 bytes read, no
|
|
769
|
+
* decode); `columns`/`rows`/`cell` need it, so they are absent when the file
|
|
770
|
+
* does not resolve under the configured roots.
|
|
771
|
+
*
|
|
772
|
+
* The grid is driven by `framesize`, the property the vanilla gui trees
|
|
773
|
+
* actually carry (both harvested titles ship it; neither ships `noofframes`).
|
|
774
|
+
*/
|
|
775
|
+
export interface GuiTextureInfo {
|
|
776
|
+
/** The path as authored, mod-relative, the way the engine reads it. */
|
|
777
|
+
path: string;
|
|
778
|
+
/** Which fill it belongs to. */
|
|
779
|
+
source: "fill" | "background";
|
|
780
|
+
/** Absolute file it resolved to: mod, then parent mods (last first), then the game. */
|
|
781
|
+
file?: string;
|
|
782
|
+
/** Sheet pixel size from the DDS header. */
|
|
783
|
+
width?: number;
|
|
784
|
+
height?: number;
|
|
785
|
+
/** `framesize = { w h }`: the grid's cell size. */
|
|
786
|
+
framesize?: [number, number];
|
|
787
|
+
/** Grid shape, row-major: floor(width/cellW) x floor(height/cellH). */
|
|
788
|
+
columns?: number;
|
|
789
|
+
rows?: number;
|
|
790
|
+
/** The 1-based frame the widget shows (`frame`, default 1), clamped to the grid. */
|
|
791
|
+
frame?: number;
|
|
792
|
+
/** That frame's cell in texture pixels. */
|
|
793
|
+
cell?: { x: number; y: number; w: number; h: number };
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
export interface GuiWidgetInfo {
|
|
797
|
+
key: string;
|
|
798
|
+
name?: string;
|
|
799
|
+
/** The base-type chain the key resolves through, derived-most first. */
|
|
800
|
+
typeChain: string[];
|
|
801
|
+
/**
|
|
802
|
+
* Effective properties in expansion order, last-in-wins per key: exactly the
|
|
803
|
+
* values the engine laid the widget out with, so the inspector cannot show a
|
|
804
|
+
* row the canvas did not use.
|
|
805
|
+
*/
|
|
806
|
+
properties: GuiWidgetProperty[];
|
|
807
|
+
/**
|
|
808
|
+
* Textures the widget draws (its own fill first, then its background), with
|
|
809
|
+
* frame-sheet geometry. `[]` when it draws none; absent only from a server
|
|
810
|
+
* that predates the field.
|
|
811
|
+
*/
|
|
812
|
+
textures?: GuiTextureInfo[];
|
|
813
|
+
/**
|
|
814
|
+
* Why the widget's rect is where it is. Present only when the request asked
|
|
815
|
+
* for it (`placement: true`) AND the layout actually reached the widget: a
|
|
816
|
+
* declaration inside a `tooltipwidget` or a subtree the engine skips has a
|
|
817
|
+
* source line but no rect.
|
|
818
|
+
*/
|
|
819
|
+
placement?: GuiPlacement;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Request: what a `.gui` document reaches on the SCRIPT side;
|
|
824
|
+
* {@link GuiDependenciesParams} -> {@link GuiDependenciesResult}. The forward
|
|
825
|
+
* half of the dependency surface; the reverse (script definition -> the .gui
|
|
826
|
+
* paths using it) is `paradox/dependencies` with `guiUses: true`, so both
|
|
827
|
+
* directions come out of the same scripted_gui link.
|
|
828
|
+
*/
|
|
829
|
+
export const guiDependenciesRequest = "paradox/guiDependencies";
|
|
830
|
+
export interface GuiDependenciesParams {
|
|
831
|
+
/** For display only; the text is authoritative. */
|
|
832
|
+
uri: string;
|
|
833
|
+
text: string;
|
|
834
|
+
/**
|
|
835
|
+
* Restrict the answer to one widget's SOURCE subtree, addressed by the
|
|
836
|
+
* 0-based line of its own statement (`GuiLayoutNode.line`). Absent = the
|
|
837
|
+
* whole document. A line carrying no widget answers with empty lists.
|
|
838
|
+
*/
|
|
839
|
+
line?: number;
|
|
840
|
+
}
|
|
841
|
+
/** A scripted_gui the document calls, and what it hands control to. */
|
|
842
|
+
export interface GuiScriptedGuiRow {
|
|
843
|
+
name: string;
|
|
844
|
+
/** Definition site; absent when the index has no scripted_gui by that name. */
|
|
845
|
+
file?: string;
|
|
846
|
+
line?: number;
|
|
847
|
+
/** 0-based lines in the REQUESTED document that call it. */
|
|
848
|
+
callLines: number[];
|
|
849
|
+
/** Call sites across every `.gui` file the layout store scanned. */
|
|
850
|
+
uses: number;
|
|
851
|
+
/** Events / on_actions the scripted_gui's own blocks hand control to. */
|
|
852
|
+
chains: GuiEventChain[];
|
|
853
|
+
}
|
|
854
|
+
/** An event or on_action a scripted_gui reaches, and how. */
|
|
855
|
+
export interface GuiEventChain {
|
|
856
|
+
name: string;
|
|
857
|
+
kind: "event" | "on_action";
|
|
858
|
+
file?: string;
|
|
859
|
+
line?: number;
|
|
860
|
+
/**
|
|
861
|
+
* The scripted effects traversed to get there, outermost first. Empty =
|
|
862
|
+
* "directly"; `["effect_a", "effect_b"]` renders as "via effect_a -> effect_b".
|
|
863
|
+
*/
|
|
864
|
+
via: string[];
|
|
865
|
+
}
|
|
866
|
+
/** A localization key the document names, checked against the loc index. */
|
|
867
|
+
export interface GuiLocRow {
|
|
868
|
+
key: string;
|
|
869
|
+
/** The gui property that named it (`text`, `tooltip`). */
|
|
870
|
+
prop: string;
|
|
871
|
+
/** 0-based line in the requested document. */
|
|
872
|
+
line: number;
|
|
873
|
+
/** No `loc_key` definition anywhere in the index. */
|
|
874
|
+
missing: boolean;
|
|
875
|
+
/** The resolved text, when the index has one. */
|
|
876
|
+
value?: string;
|
|
877
|
+
}
|
|
878
|
+
export interface GuiDependenciesResult {
|
|
879
|
+
/** The widget the answer is scoped to; absent for a whole-document answer. */
|
|
880
|
+
widget?: { key: string; name?: string; line: number };
|
|
881
|
+
scriptedGuis: GuiScriptedGuiRow[];
|
|
882
|
+
locKeys: GuiLocRow[];
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Request: the widget names a designer palette may offer for THIS document;
|
|
887
|
+
* {@link GuiVocabularyParams} -> {@link GuiVocabularyResult}.
|
|
888
|
+
*
|
|
889
|
+
* Every name is harvested, never listed by hand: the bundled per-game widget
|
|
890
|
+
* schema (`data/<game>/guiSchema.json`, built from the vanilla `gui/` tree)
|
|
891
|
+
* plus the requested document's own `template` and `type` declarations. A
|
|
892
|
+
* palette entry is therefore always a widget the game knows.
|
|
893
|
+
*/
|
|
894
|
+
export const guiVocabularyRequest = "paradox/guiVocabulary";
|
|
895
|
+
export interface GuiVocabularyParams {
|
|
896
|
+
/** For display only; the text is authoritative. */
|
|
897
|
+
uri: string;
|
|
898
|
+
text: string;
|
|
899
|
+
}
|
|
900
|
+
export interface GuiVocabularyEntry {
|
|
901
|
+
name: string;
|
|
902
|
+
/** `builtin` = the vanilla harvest; `type`/`template` = this document declares it. */
|
|
903
|
+
kind: "builtin" | "type" | "template";
|
|
904
|
+
/** How many times the vanilla gui tree writes it (`builtin` only). */
|
|
905
|
+
count?: number;
|
|
906
|
+
/** The base widget key a `type` derives from. */
|
|
907
|
+
base?: string;
|
|
908
|
+
/** Declared in the requested document itself. */
|
|
909
|
+
local?: boolean;
|
|
910
|
+
/**
|
|
911
|
+
* The vanilla tree writes widgets inside it, so it can hold children: what a
|
|
912
|
+
* "wrap in a container" menu offers. Derived from the harvest's own child
|
|
913
|
+
* counts, not from a list of container names.
|
|
914
|
+
*/
|
|
915
|
+
container?: boolean;
|
|
916
|
+
}
|
|
917
|
+
export interface GuiVocabularyResult {
|
|
918
|
+
/**
|
|
919
|
+
* The document's own declarations first, then the harvested types by vanilla
|
|
920
|
+
* usage. Capped; `total` gives the real count, so a UI states what it hid.
|
|
921
|
+
*/
|
|
922
|
+
entries: GuiVocabularyEntry[];
|
|
923
|
+
total: number;
|
|
924
|
+
/**
|
|
925
|
+
* Widget type -> the property names the harvest saw on it, most used first
|
|
926
|
+
* and capped: what an inspector's add-property row completes from. Only the
|
|
927
|
+
* types THIS DOCUMENT names are here (the keys it writes blocks under, plus
|
|
928
|
+
* the bases of its own `type X = base` declarations), because the harvest
|
|
929
|
+
* holds hundreds of types and this answer is re-asked after every layout.
|
|
930
|
+
* The server always sends it (empty for a game with no harvest); it is
|
|
931
|
+
* optional only so older recorded responses stay type-valid.
|
|
932
|
+
*/
|
|
933
|
+
properties?: Record<string, string[]>;
|
|
934
|
+
/**
|
|
935
|
+
* The vanilla tree's most-used property names overall, most used first and
|
|
936
|
+
* capped: the fallback ranking for a widget whose type the harvest has never
|
|
937
|
+
* seen, so completion still offers something real rather than nothing.
|
|
938
|
+
*/
|
|
939
|
+
commonProperties?: string[];
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Render-ready previews of palette entries: one instance of each entry laid
|
|
944
|
+
* out in a synthetic document that keeps the requested document's own
|
|
945
|
+
* declarations (so a local template previews with its real base). The
|
|
946
|
+
* result is an ordinary node tree the client draws with the same painter as
|
|
947
|
+
* the canvas. Entries a synthetic document cannot stand up (nothing to show,
|
|
948
|
+
* zero size, a type the store lacks) come back with `node: null` and a
|
|
949
|
+
* `reason`. Capped per request (GUI_PREVIEW_MAX); ask for the visible page.
|
|
950
|
+
*/
|
|
951
|
+
export const guiPreviewRequest = "paradox/guiPreview";
|
|
952
|
+
export const GUI_PREVIEW_MAX = 48;
|
|
953
|
+
export interface GuiPreviewEntry {
|
|
954
|
+
name: string;
|
|
955
|
+
/** `raw`: `fragment` is `.gui` text (a saved component) laid out as is. */
|
|
956
|
+
kind: "builtin" | "type" | "template" | "raw";
|
|
957
|
+
fragment?: string;
|
|
958
|
+
}
|
|
959
|
+
export interface GuiPreviewParams {
|
|
960
|
+
/** For display only; the text is authoritative. */
|
|
961
|
+
uri: string;
|
|
962
|
+
text: string;
|
|
963
|
+
entries: GuiPreviewEntry[];
|
|
964
|
+
}
|
|
965
|
+
export interface GuiPreview {
|
|
966
|
+
name: string;
|
|
967
|
+
node: GuiLayoutNode | null;
|
|
968
|
+
/** Texture paths the node tree references (mod-relative). */
|
|
969
|
+
textures: string[];
|
|
970
|
+
reason?: string;
|
|
971
|
+
}
|
|
972
|
+
export interface GuiPreviewResult {
|
|
973
|
+
previews: GuiPreview[];
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Preview values read out of a save game, so a designer draws
|
|
978
|
+
* `[GetPlayer.GetName]` as "Great Britain" instead of a placeholder chip.
|
|
979
|
+
*
|
|
980
|
+
* `values` is keyed by datafunction chain WITHOUT brackets, exactly the shape
|
|
981
|
+
* {@link GuiLayoutParams.previewValues} takes, so a client hands the answer
|
|
982
|
+
* straight back to the next layout request. A chain the save has no field for
|
|
983
|
+
* is absent: a preview shows what is knowable and never invents a value.
|
|
984
|
+
*
|
|
985
|
+
* The server streams the file and parses only the few blocks it needs (a big
|
|
986
|
+
* campaign runs ~115 MB), and caches the answer per file and mtime.
|
|
987
|
+
* Ironman and binary saves are refused with `error` set; melting them is a
|
|
988
|
+
* different tool.
|
|
989
|
+
*/
|
|
990
|
+
export const guiSaveValuesRequest = "paradox/guiSaveValues";
|
|
991
|
+
export interface GuiSaveValuesParams {
|
|
992
|
+
/** Absolute path to the save file. */
|
|
993
|
+
path: string;
|
|
994
|
+
}
|
|
995
|
+
export interface GuiSaveValuesResult {
|
|
996
|
+
/** Datafunction chain without brackets -> display text. */
|
|
997
|
+
values: Record<string, string>;
|
|
998
|
+
/** What the values came from, for a UI to name the save it is showing. */
|
|
999
|
+
source: {
|
|
1000
|
+
/** The campaign's name as the save's meta data states it. */
|
|
1001
|
+
name: string;
|
|
1002
|
+
/** The in-game date, already formatted ("21 January 1836"). */
|
|
1003
|
+
date: string;
|
|
1004
|
+
/** The game the values were read for. */
|
|
1005
|
+
game: string;
|
|
1006
|
+
};
|
|
1007
|
+
/** Set when the save cannot be read (ironman, binary, unreadable). */
|
|
1008
|
+
error?: string;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Request: source edits for a `.gui` designer gesture;
|
|
1013
|
+
* {@link GuiSourceEditParams} -> {@link GuiSourceEditResult}, null when the
|
|
1014
|
+
* request itself makes no sense (an unknown op). The server never writes: it
|
|
1015
|
+
* returns offsets into the text it was handed and the host applies them, which
|
|
1016
|
+
* keeps undo, dirty state and the live preview in the editor (EMBEDDING.md,
|
|
1017
|
+
* host-owns-text).
|
|
1018
|
+
*
|
|
1019
|
+
* Every edit is surgical, over the exact span the source model recorded, so
|
|
1020
|
+
* untouched bytes stay byte-identical: comments, CRLF, tabs-vs-spaces and
|
|
1021
|
+
* single-line bodies all survive a write.
|
|
1022
|
+
*/
|
|
1023
|
+
export const guiSourceEditRequest = "paradox/guiSourceEdit";
|
|
1024
|
+
export interface GuiSourceEditParams {
|
|
1025
|
+
/** For display only; the text is authoritative. */
|
|
1026
|
+
uri: string;
|
|
1027
|
+
/** Authoritative document text every offset refers to. */
|
|
1028
|
+
text: string;
|
|
1029
|
+
/** One op. Mutually exclusive with {@link ops}; sending both answers null. */
|
|
1030
|
+
op?: GuiSourceOp;
|
|
1031
|
+
/**
|
|
1032
|
+
* A BATCH: several ops computed against this one text and answered as one
|
|
1033
|
+
* edit set, which is what makes a multi-widget gesture one document change
|
|
1034
|
+
* and one undo step. Every op gets a verdict of its own in
|
|
1035
|
+
* {@link GuiSourceEditResult.results}, so a refusal is per op and the rest
|
|
1036
|
+
* still apply. Order matters: the ops are computed in the order given, and a
|
|
1037
|
+
* later one whose bytes a earlier one already changes is refused rather than
|
|
1038
|
+
* silently dropped.
|
|
1039
|
+
*/
|
|
1040
|
+
ops?: GuiSourceOp[];
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/** One surgical replacement: replace `[start, end)` with `newText`. */
|
|
1044
|
+
export interface GuiTextEdit {
|
|
1045
|
+
/** UTF-16 offsets into the request text. */
|
|
1046
|
+
start: number;
|
|
1047
|
+
end: number;
|
|
1048
|
+
newText: string;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* What to do. `line` is the 0-based line of the target widget's own statement,
|
|
1053
|
+
* the same `line` {@link GuiLayoutNode} reports; a node with no line of its own
|
|
1054
|
+
* (spliced in from a template or a type) has no source to edit and is refused.
|
|
1055
|
+
* `index` counts SOURCE children, not the template-expanded ones a preview
|
|
1056
|
+
* shows; out of range appends.
|
|
1057
|
+
*/
|
|
1058
|
+
export type GuiSourceOp =
|
|
1059
|
+
/** Set or (with a null value) remove properties on one widget. */
|
|
1060
|
+
| { kind: "setProperties"; line: number; properties: { key: string; value: string | null }[] }
|
|
1061
|
+
/** Move a source child of the widget on `line` from one index to another. */
|
|
1062
|
+
| { kind: "reorder"; line: number; from: number; to: number }
|
|
1063
|
+
| { kind: "insert"; line: number; widget: GuiNewWidget; index?: number }
|
|
1064
|
+
/** Paste `.gui` text as a child, re-indented for the destination. */
|
|
1065
|
+
| { kind: "insertRaw"; line: number; fragment: string; index?: number }
|
|
1066
|
+
| { kind: "delete"; line: number }
|
|
1067
|
+
/** Copy the widget in as its own next sibling, optionally renamed. */
|
|
1068
|
+
| { kind: "duplicate"; line: number; name?: string }
|
|
1069
|
+
/** Wrap the widgets on `lines` (siblings) in a fresh container. */
|
|
1070
|
+
| { kind: "wrap"; lines: number[]; container: GuiNewWidget }
|
|
1071
|
+
/** Read-only: the widget's block, verbatim, for a clipboard. */
|
|
1072
|
+
| { kind: "blockText"; line: number };
|
|
1073
|
+
|
|
1074
|
+
/** A declaration to write: `type = { properties }`, properties in order. */
|
|
1075
|
+
export interface GuiNewWidget {
|
|
1076
|
+
type: string;
|
|
1077
|
+
properties?: [string, string][];
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* For a single `op`, exactly one of `edits` and `refused` is present. A refusal
|
|
1082
|
+
* is an ANSWER, not an error: it names why the gesture would not do what it
|
|
1083
|
+
* looks like it does (a box owns its children's slots, a content-sized type
|
|
1084
|
+
* ignores an explicit size, a type definition other files use). `warning` rides
|
|
1085
|
+
* along with a write that went ahead but is only half honoured.
|
|
1086
|
+
*
|
|
1087
|
+
* For a BATCH (`ops`), `results` is present with one entry per op in the same
|
|
1088
|
+
* order, `edits` is every applied op's edits together (apply them as ONE
|
|
1089
|
+
* change), and `warning` joins the warnings. Top-level `refused` then names
|
|
1090
|
+
* only a whole-request failure (a document that does not parse, an empty
|
|
1091
|
+
* batch): a per-op refusal lives in its own entry and does not stop the others.
|
|
1092
|
+
*/
|
|
1093
|
+
export interface GuiSourceEditResult {
|
|
1094
|
+
edits?: GuiTextEdit[];
|
|
1095
|
+
refused?: string;
|
|
1096
|
+
warning?: string;
|
|
1097
|
+
/** `blockText` only: the copied block. */
|
|
1098
|
+
blockText?: string;
|
|
1099
|
+
/** Batch only: one verdict per requested op, in request order. */
|
|
1100
|
+
results?: GuiSourceOpResult[];
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/** One op's own answer inside a batch. */
|
|
1104
|
+
export interface GuiSourceOpResult {
|
|
1105
|
+
/** Why this op wrote nothing. The others in the batch still applied. */
|
|
1106
|
+
refused?: string;
|
|
1107
|
+
/** This op wrote, and is only half honoured. */
|
|
1108
|
+
warning?: string;
|
|
1109
|
+
/** This op's contribution to the combined `edits`; empty when it wrote nothing. */
|
|
1110
|
+
edits: GuiTextEdit[];
|
|
1111
|
+
/** `blockText` only: the copied block. */
|
|
1112
|
+
blockText?: string;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Request: text edit for a preview interaction (drag / property change);
|
|
1117
|
+
* {@link GuiWidgetEditParams} -> {@link GuiWidgetEditResult} (null when the
|
|
1118
|
+
* widget or property cannot be edited). The client applies the offsets via
|
|
1119
|
+
* WorkspaceEdit so undo and the live preview loop stay in the editor.
|
|
1120
|
+
*
|
|
1121
|
+
* @deprecated Use {@link guiSourceEditRequest} with a `setProperties` op. This
|
|
1122
|
+
* is a thin alias over the same core, kept for hosts already wired to it: it
|
|
1123
|
+
* can only write the `position`/`size` pair and returns one edit or null, so a
|
|
1124
|
+
* refusal reaches the caller as a bare null with no reason attached.
|
|
1125
|
+
*/
|
|
1126
|
+
export const guiWidgetEditRequest = "paradox/guiWidgetEdit";
|
|
1127
|
+
export interface GuiWidgetEditParams {
|
|
1128
|
+
uri: string;
|
|
1129
|
+
/** Authoritative document text the offsets refer to. */
|
|
1130
|
+
text: string;
|
|
1131
|
+
/** 0-based line of the widget's instance statement (GuiLayoutNode.line). */
|
|
1132
|
+
line: number;
|
|
1133
|
+
/** Pair property to set. */
|
|
1134
|
+
property: "position" | "size";
|
|
1135
|
+
values: [number, number];
|
|
1136
|
+
}
|
|
1137
|
+
export interface GuiWidgetEditResult {
|
|
1138
|
+
/** UTF-16 offsets into the request text. */
|
|
1139
|
+
start: number;
|
|
1140
|
+
end: number;
|
|
1141
|
+
newText: string;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/** Request: event graph; {@link EventGraphParams} -> {@link EventGraph}. */
|
|
1145
|
+
export const eventGraphRequest = "paradox/eventGraph";
|
|
1146
|
+
export interface EventGraphParams {
|
|
1147
|
+
/** Focus definition (event id / on_action name); with namespace, either works. */
|
|
1148
|
+
root?: string;
|
|
1149
|
+
/** Restrict to an event namespace. */
|
|
1150
|
+
namespace?: string;
|
|
1151
|
+
/** Restrict to one workspace mod (absolute root path). */
|
|
1152
|
+
modRoot?: string | null;
|
|
1153
|
+
maxNodes?: number;
|
|
1154
|
+
/** Also read each mod event's `theme`. Off by default: it costs one parse per
|
|
1155
|
+
* event file, and only a client that draws the theme's art needs it. */
|
|
1156
|
+
themes?: boolean;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* One row of a mod event's card, in EXECUTION order (immediate, then the
|
|
1160
|
+
* options, then after) rather than file order. `line` is the join key an edge
|
|
1161
|
+
* uses to anchor at the row that fires it ({@link EventGraphEdge.fromLine}).
|
|
1162
|
+
*/
|
|
1163
|
+
export interface EventGraphStep {
|
|
1164
|
+
phase: "immediate" | "option" | "after";
|
|
1165
|
+
/** Option ordinal within the event, 0-based. */
|
|
1166
|
+
index?: number;
|
|
1167
|
+
/** The option's localized text, when resolvable. */
|
|
1168
|
+
text?: string;
|
|
1169
|
+
/** 0-based line of the step's key in the event's file. */
|
|
1170
|
+
line: number;
|
|
1171
|
+
}
|
|
1172
|
+
export interface EventGraphNode {
|
|
1173
|
+
id: string;
|
|
1174
|
+
kind: string;
|
|
1175
|
+
source: "vanilla" | "parent" | "mod";
|
|
1176
|
+
file?: string;
|
|
1177
|
+
line?: number;
|
|
1178
|
+
/** Localized title (best-effort: <id>.t / <id>_t / <id>.title lookups). */
|
|
1179
|
+
title?: string;
|
|
1180
|
+
/** The event's declared `theme`, when the request asked for themes. */
|
|
1181
|
+
theme?: string;
|
|
1182
|
+
/** How many `option` blocks this definition has (mod-side definitions only). */
|
|
1183
|
+
options?: number;
|
|
1184
|
+
/** The first keys of its `trigger` block, e.g. `is_adult, has_trait…`; absent = no trigger. */
|
|
1185
|
+
triggerSummary?: string;
|
|
1186
|
+
/** How many other nodes of this graph it fires; absent when it fires none. */
|
|
1187
|
+
fires?: number;
|
|
1188
|
+
/** The card's rows (mod events only), capped; {@link EventGraphNode.options} is the true count. */
|
|
1189
|
+
steps?: EventGraphStep[];
|
|
1190
|
+
}
|
|
1191
|
+
export interface EventGraphEdge {
|
|
1192
|
+
from: string;
|
|
1193
|
+
to: string;
|
|
1194
|
+
/** The referencing field (trigger_event, events, on_actions...). */
|
|
1195
|
+
via: string;
|
|
1196
|
+
/** Where in the source event the reference sits: an option's text, or immediate/after/… */
|
|
1197
|
+
label?: string;
|
|
1198
|
+
/** The block the reference sits in, normalized (option/immediate/after/effect/…). */
|
|
1199
|
+
phase?: string;
|
|
1200
|
+
/** 0-based line of that block's key: matches a step's `line` on the source node. */
|
|
1201
|
+
fromLine?: number;
|
|
1202
|
+
/** The trigger_event delay at this site, pre-rendered short: "30d", "7–14d", "2mo", "1y". */
|
|
1203
|
+
delay?: string;
|
|
1204
|
+
/** random_events weight at this site (raw script number). */
|
|
1205
|
+
weight?: number;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* What a query box may offer: the whole mod-side vocabulary of the graph, NOT
|
|
1209
|
+
* the ids this particular query selected. It is the same pass that collects the
|
|
1210
|
+
* mod's graph definitions, so a client gets it without a second request.
|
|
1211
|
+
*/
|
|
1212
|
+
export interface EventGraphSuggestions {
|
|
1213
|
+
/** Mod-side event / on_action / decision ids, sorted, capped at 2000. */
|
|
1214
|
+
ids: string[];
|
|
1215
|
+
/** The event namespaces those ids belong to, sorted. */
|
|
1216
|
+
namespaces: string[];
|
|
1217
|
+
}
|
|
1218
|
+
export interface EventGraph {
|
|
1219
|
+
nodes: EventGraphNode[];
|
|
1220
|
+
edges: EventGraphEdge[];
|
|
1221
|
+
truncated: boolean;
|
|
1222
|
+
/** Absent from servers that predate it; a client must tolerate that. */
|
|
1223
|
+
suggestions?: EventGraphSuggestions;
|
|
1224
|
+
/**
|
|
1225
|
+
* Set only when the graph is empty AND the server knows why: the queried
|
|
1226
|
+
* namespace/root exists, but outside what the graph shows (another workspace
|
|
1227
|
+
* mod when a focus filter is on, a dependency mod, or vanilla). One
|
|
1228
|
+
* user-readable sentence; absent = the generic "nothing found" story.
|
|
1229
|
+
*/
|
|
1230
|
+
emptyReason?: string;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* Request: the value sets an event editor may offer; {@link EventVocabularyParams}
|
|
1235
|
+
* to {@link EventVocabularyResult}.
|
|
1236
|
+
*
|
|
1237
|
+
* Everything in the answer is DERIVED: the key lists come from the active
|
|
1238
|
+
* profile's structure table, the field value sets from the schema's reference
|
|
1239
|
+
* fields resolved through the definition index, and the effect/trigger lists
|
|
1240
|
+
* from the user's script_docs (or the bundled wiki fallback). Nothing here is a
|
|
1241
|
+
* hand-written name list, so a game patch that adds a theme or an effect shows
|
|
1242
|
+
* up without a release.
|
|
1243
|
+
*/
|
|
1244
|
+
/**
|
|
1245
|
+
* Request: the illustration an event theme puts behind its window;
|
|
1246
|
+
* {@link EventBannerParams} to {@link EventBannerResult}.
|
|
1247
|
+
*
|
|
1248
|
+
* Resolved through the game's own two hops (event_themes -> event_backgrounds),
|
|
1249
|
+
* taking the last `background` block that carries no `trigger`, which is the
|
|
1250
|
+
* file's own unconditional fallback. `texture` is the engine's mod-relative
|
|
1251
|
+
* path, exactly as a `.gui` file would spell it, so a client resolves it with
|
|
1252
|
+
* the same mod-then-game lookup it uses for any other texture. A theme that
|
|
1253
|
+
* resolves to nothing answers `reason` instead: the caller is expected to say
|
|
1254
|
+
* so rather than draw a picture that is not the event's.
|
|
1255
|
+
*/
|
|
1256
|
+
export const eventBannerRequest = "paradox/eventBanner";
|
|
1257
|
+
export interface EventBannerParams {
|
|
1258
|
+
/** Theme name as the event writes it (`theme = intrigue`). */
|
|
1259
|
+
theme: string;
|
|
1260
|
+
}
|
|
1261
|
+
export interface EventBannerResult {
|
|
1262
|
+
theme: string;
|
|
1263
|
+
/** Mod-relative texture path, absent when nothing resolved. */
|
|
1264
|
+
texture?: string;
|
|
1265
|
+
/** Why nothing resolved. Present exactly when `texture` is absent. */
|
|
1266
|
+
reason?: string;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
export const eventVocabularyRequest = "paradox/eventVocabulary";
|
|
1270
|
+
export interface EventVocabularyParams {
|
|
1271
|
+
/** Restrict definition-backed value sets to one workspace mod (plus vanilla). */
|
|
1272
|
+
modRoot?: string | null;
|
|
1273
|
+
}
|
|
1274
|
+
/** One offerable value with the one-line docs an editor shows beside it. */
|
|
1275
|
+
export interface EventVocabularyItem {
|
|
1276
|
+
value: string;
|
|
1277
|
+
/** Documentation, capped. Empty when the source has none; never invented. */
|
|
1278
|
+
doc?: string;
|
|
1279
|
+
/** Dimmer right-hand label: where the value comes from (mod / vanilla / a kind). */
|
|
1280
|
+
hint?: string;
|
|
1281
|
+
}
|
|
1282
|
+
/** Caps: an editor lists a page at a time, and these ride on every open. */
|
|
1283
|
+
export const EVENT_VOCABULARY_MAX_TOKENS = 600;
|
|
1284
|
+
export const EVENT_VOCABULARY_MAX_VALUES = 400;
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Request: the value set a VALUE belongs to, resolved through the definition
|
|
1288
|
+
* index; {@link EventValueOptionsParams} -> {@link EventValueOptionsResult} |
|
|
1289
|
+
* null. The static vocabulary maps a KEY to its values, which only works where
|
|
1290
|
+
* the schema knows the key's context (an event's or option's own fields). Deep
|
|
1291
|
+
* inside an effect tree the same key name means something else (`type` in
|
|
1292
|
+
* `random_secret` is a secret, not an event type), so there the editor asks
|
|
1293
|
+
* about the value it already has: `secret_cultivator` is an indexed `secret`,
|
|
1294
|
+
* and the answer is every secret the index knows. Null = the value resolves to
|
|
1295
|
+
* nothing enumerable; the editor falls back to a free input.
|
|
1296
|
+
*/
|
|
1297
|
+
export const eventValueOptionsRequest = "paradox/eventValueOptions";
|
|
1298
|
+
export interface EventValueOptionsParams {
|
|
1299
|
+
value: string;
|
|
1300
|
+
/** Restrict mod-side entries to one workspace mod (plus vanilla/parents). */
|
|
1301
|
+
modRoot?: string | null;
|
|
1302
|
+
}
|
|
1303
|
+
export interface EventValueOptionsResult {
|
|
1304
|
+
/** The definition kind the value resolved to (trait, secret, faith…). */
|
|
1305
|
+
kind: string;
|
|
1306
|
+
/** Every indexed definition of that kind, mod entries first, capped. */
|
|
1307
|
+
items: EventVocabularyItem[];
|
|
1308
|
+
}
|
|
1309
|
+
export interface EventVocabularyResult {
|
|
1310
|
+
/** Keys valid at an event's top level, most used first. */
|
|
1311
|
+
eventKeys: EventVocabularyItem[];
|
|
1312
|
+
/** Keys valid inside an `option` block, most used first. */
|
|
1313
|
+
optionKeys: EventVocabularyItem[];
|
|
1314
|
+
/**
|
|
1315
|
+
* Key to the values that key accepts, for the keys whose value set is known:
|
|
1316
|
+
* a declared enumeration, or a reference field resolved through the index
|
|
1317
|
+
* (`theme` gives every indexed event_theme). Keys with a free value are absent.
|
|
1318
|
+
*/
|
|
1319
|
+
values: Record<string, EventVocabularyItem[]>;
|
|
1320
|
+
/** Effect tokens, most used first, capped at EVENT_VOCABULARY_MAX_TOKENS. */
|
|
1321
|
+
effects: EventVocabularyItem[];
|
|
1322
|
+
/** Trigger tokens, same ordering and cap. */
|
|
1323
|
+
triggers: EventVocabularyItem[];
|
|
1324
|
+
/** Saved scopes the mod writes (`save_scope_as`), sorted. */
|
|
1325
|
+
savedScopes: EventVocabularyItem[];
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* Request: dependency explorer for any indexed definition;
|
|
1330
|
+
* {@link DependenciesParams} -> {@link DependenciesResult}. Cursor-driven
|
|
1331
|
+
* (uri + position) or by name (optionally disambiguated by kind).
|
|
1332
|
+
*/
|
|
1333
|
+
export const dependenciesRequest = "paradox/dependencies";
|
|
1334
|
+
export interface DependenciesParams {
|
|
1335
|
+
/** Resolve the definition under this cursor position. */
|
|
1336
|
+
uri?: string;
|
|
1337
|
+
position?: { line: number; character: number };
|
|
1338
|
+
/** Fallback: look the definition up by name (optionally by kind). */
|
|
1339
|
+
name?: string;
|
|
1340
|
+
kind?: string;
|
|
1341
|
+
/**
|
|
1342
|
+
* Also resolve {@link DependenciesResult.guiUses}: the `.gui` call sites that
|
|
1343
|
+
* reach this definition through a scripted_gui. Off by default, it walks the
|
|
1344
|
+
* scripted_gui definitions that any .gui file calls, which the plain
|
|
1345
|
+
* dependency answer does not need.
|
|
1346
|
+
*/
|
|
1347
|
+
guiUses?: boolean;
|
|
1348
|
+
}
|
|
1349
|
+
export interface DependencyDef {
|
|
1350
|
+
name: string;
|
|
1351
|
+
kind: string;
|
|
1352
|
+
file: string;
|
|
1353
|
+
/** 0-based. */
|
|
1354
|
+
line: number;
|
|
1355
|
+
}
|
|
1356
|
+
export interface DependencyItem {
|
|
1357
|
+
name: string;
|
|
1358
|
+
file: string;
|
|
1359
|
+
/** 0-based. */
|
|
1360
|
+
line: number;
|
|
1361
|
+
}
|
|
1362
|
+
export interface DependencyGroup {
|
|
1363
|
+
kind: string;
|
|
1364
|
+
items: DependencyItem[];
|
|
1365
|
+
}
|
|
1366
|
+
export interface DependenciesResult {
|
|
1367
|
+
/** The resolved definition, or null when nothing matches the cursor/name. */
|
|
1368
|
+
def: DependencyDef | null;
|
|
1369
|
+
/** Mod definitions/sites that reference `def` (mod files only; vanilla
|
|
1370
|
+
* references aren't indexed, AD-4). Grouped by the containing definition's
|
|
1371
|
+
* kind, else by file. */
|
|
1372
|
+
dependents: DependencyGroup[];
|
|
1373
|
+
/** Named definitions referenced inside `def`'s block, grouped by target kind. */
|
|
1374
|
+
dependencies: DependencyGroup[];
|
|
1375
|
+
/**
|
|
1376
|
+
* The GUI side of the same question, present only when `guiUses` was asked
|
|
1377
|
+
* for: which `.gui` files reach `def`, and through which scripted_gui. `[]`
|
|
1378
|
+
* is the honest "none found"; the field is absent when it was not requested.
|
|
1379
|
+
*/
|
|
1380
|
+
guiUses?: GuiUseSite[];
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/**
|
|
1384
|
+
* One `.gui` call site that reaches a script definition. The link is always a
|
|
1385
|
+
* scripted_gui: `.gui` invokes script through `GetScriptedGui('name')` and
|
|
1386
|
+
* nothing else, so the path is `file:line -> scripted_gui -> [effects] -> def`.
|
|
1387
|
+
*/
|
|
1388
|
+
export interface GuiUseSite {
|
|
1389
|
+
/** Absolute path of the `.gui` file holding the call. */
|
|
1390
|
+
file: string;
|
|
1391
|
+
/** 0-based line of the `GetScriptedGui(...)` call. */
|
|
1392
|
+
line: number;
|
|
1393
|
+
/** The scripted_gui the call names. */
|
|
1394
|
+
scriptedGui: string;
|
|
1395
|
+
/**
|
|
1396
|
+
* The scripted effects between that scripted_gui and the definition,
|
|
1397
|
+
* outermost first. Empty means the scripted_gui's own blocks name it
|
|
1398
|
+
* ("directly"); `["effect_a", "effect_b"]` renders as
|
|
1399
|
+
* "via effect_a -> effect_b".
|
|
1400
|
+
*/
|
|
1401
|
+
via: string[];
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Request: the inferred scope chain at a cursor position;
|
|
1406
|
+
* {@link ScopeAtParams} -> {@link ScopeAtResult} | null. Answers for OPEN
|
|
1407
|
+
* script documents only (the server reads the client's text, not the disk);
|
|
1408
|
+
* null means "not open / not a script document", which a status bar renders as
|
|
1409
|
+
* nothing rather than as an error.
|
|
1410
|
+
*
|
|
1411
|
+
* This is a read-out of the same inference completion, hover and inlay hints
|
|
1412
|
+
* run at a position: it ranks and annotates, never diagnoses, and never
|
|
1413
|
+
* asserts more than the derived link tables actually say.
|
|
1414
|
+
*/
|
|
1415
|
+
export const scopeAtRequest = "paradox/scopeAt";
|
|
1416
|
+
export interface ScopeAtParams {
|
|
1417
|
+
uri: string;
|
|
1418
|
+
/** 0-based, as in LSP. */
|
|
1419
|
+
position: { line: number; character: number };
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/** One resolved step of the walk from the root scope down to the cursor. */
|
|
1423
|
+
export interface ScopeChainStep {
|
|
1424
|
+
/**
|
|
1425
|
+
* The key that produced the step: a link (`liege`), an iterator
|
|
1426
|
+
* (`every_vassal`), `root`/`prev`, a `scope:x` / `var:x` anchor, or a data
|
|
1427
|
+
* link abbreviated as `culture:…`. Absent on the FIRST step only, which is
|
|
1428
|
+
* the enclosing definition's root scope and comes from no key.
|
|
1429
|
+
*/
|
|
1430
|
+
entryKeyword?: string;
|
|
1431
|
+
/** Scopes after this step; empty = unknown. */
|
|
1432
|
+
scopes: string[];
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/** A saved scope visible in the document, with the type it resolves to. */
|
|
1436
|
+
export interface SavedScopeInfo {
|
|
1437
|
+
name: string;
|
|
1438
|
+
/** Scopes the name resolves to; empty = unknown. */
|
|
1439
|
+
scopes: string[];
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
export interface ScopeAtResult {
|
|
1443
|
+
/**
|
|
1444
|
+
* Scopes at the position. A SET, not one name: a link or iterator with
|
|
1445
|
+
* several documented output scopes stays ambiguous instead of guessing, and
|
|
1446
|
+
* an EMPTY array means unknown, which is a first-class answer here. Render
|
|
1447
|
+
* several as `a|b` and none as "unknown".
|
|
1448
|
+
*/
|
|
1449
|
+
scopes: string[];
|
|
1450
|
+
/** The walk, outermost (root) first, one entry per scope-changing step. */
|
|
1451
|
+
chain: ScopeChainStep[];
|
|
1452
|
+
/**
|
|
1453
|
+
* Saved scopes visible in the document, name-sorted: every `save_scope_as` /
|
|
1454
|
+
* `save_scope_value_as` site in the file plus the engine-provided ambient
|
|
1455
|
+
* scopes of its definition kind. NOT flow-sensitive, a save further down
|
|
1456
|
+
* the file is listed too, matching what completion and hover already offer.
|
|
1457
|
+
*/
|
|
1458
|
+
savedScopes: SavedScopeInfo[];
|
|
1459
|
+
}
|