Host Services
Extension code does not talk to Charon’s server or its UI directly. It is handed a set of services by the host, and reaches everything through them - reading and writing game data, opening dialogs and Charon’s own wizards, showing notifications, navigating, and persisting UI state between sessions.
Which object carries them depends on what the extension is:
Extension kind |
Where the services are |
|---|---|
|
|
|
|
|
|
|
The two sets overlap but are not identical: an action or a page gets the project-level services, a document gets
the document-level ones. Both are declared Partial - the host populates only what makes sense in the
context, so check every field before use.
const dialog = context.services.ui?.dialog;
if (!dialog) {
return;
}
What Each Context Carries
Service |
Action / page context |
Document control |
|---|---|---|
|
yes |
yes |
|
yes |
yes |
|
scoped to the package |
scoped to the document or its schema |
|
yes |
- |
|
yes |
- |
|
- |
yes |
|
- |
yes |
|
- |
yes |
|
yes |
yes |
serverApiClient is the raw HTTP client behind everything else. It is deliberately untyped and its surface is
not stable - use it only for something the typed services do not cover, and expect it to change.
Note
services.uiState still exists next to services.ui.state and is the very same object, kept for
packages written before ui was introduced. New code should use ui.state.
Game Data
GameDataService is the project’s data, as the server sees it - not the document currently open in a form.
Everything returns an observable.
Method |
Purpose |
|---|---|
|
The project’s schemas and settings. |
|
Documents of a schema, with |
|
One document by a unique property value. |
|
Several such lookups in one round trip. |
|
Create, update, or delete many documents of one schema. |
|
The same across several schemas at once, taking a whole game data document. |
|
Read data out in one of the export modes. |
|
Run validation and get the errors per document. |
importMode is the same choice the import wizard offers -
createAndUpdate, create, update, safeUpdate, replace, delete. exportMode likewise:
normal, publication, localization, extraction.
Tip
dryRun: true on bulkChange and import validates the whole change and reports what would happen
without writing anything. Every result carries per-document errors, so an extension can show the user
what it is about to do before it does it.
Results carry a metadataHash and a revisionHash - the state of the schemas and of the data at the time of
the call. Comparing them across two calls is how an extension notices that someone else changed the project
underneath it.
Dialogs and Wizards
ui.dialog opens modals in the host’s own chrome.
Progress
showProgress(options) returns a handle for a long operation - update(percent, message?, color?),
setFaulted(), and close(delayMs?). With cancellable: true the dialog can be dismissed and
closedHandler(cancelled) fires. See Custom Actions for the full pattern.
Your Own Dialog
showCustom(selector, data?, options?) hosts a custom element your package registered, inside a Charon dialog:
const dialogRef = dialog.showCustom<MyResult>('ext-my-dialog', { itemId }, {
title: 'Pick an item',
width: '600px',
disableClose: false,
});
const result = await firstValueFrom(dialogRef.afterClosed());
The element receives data and closes itself through dialogRef.close(result); afterClosed() emits
that result, or undefined when the dialog was dismissed.
showCodeSnippet({ sourceCode }) is a ready-made read-only viewer with copy-to-clipboard, for showing a
generated command line or a fragment of JSON.
Charon’s Wizards
An extension can open the editor’s own wizards rather than reimplementing them. Each returns a promise that
resolves true when the wizard completed and false when it was cancelled:
Method |
Opens |
|---|---|
|
Import, optionally pre-loaded with a file or text and a schema |
|
Export, optionally with a schema and format preselected |
|
The translation round trip |
|
|
|
|
|
All of them take loadPreferences and savePreferences, both defaulting to true - the wizard opens filled
in the way the user last left it, and remembers what they do this time. Pass false when the extension is
driving the wizard with its own values and should not disturb the user’s saved choices.
Notifications
ui.snackBar is the host’s transient notification strip, in matched sets of three - loadStarted /
loadSucceed / loadFailed(error), and the same for save, reload, and delete. Using them keeps
an extension’s feedback indistinguishable from the editor’s own, including how errors are rendered.
Persisted UI State
ui.state stores small pieces of state - a zoom level, a collapsed panel, a chosen layout - and gives them
back the next time the extension runs. On an action or page context it is scoped to the extension package; on a
document control it takes a scope first, 'document' or 'schema'.
state.save('projectPersonal', { zoom, collapsed });
const restored = state.load('projectPersonal');
The layer decides where the value lives and how far it travels. On load, the requested layer is checked first and lower-priority layers after it, so a personal value shadows a shared one:
Layer |
Stored in |
|---|---|
|
Session storage - gone when the tab closes. |
|
Local storage, this user. |
|
Local storage, everyone using this browser. |
|
The server, this user, this project. |
|
The server, everyone in the project. |
|
The server, this user, across the workspace. |
|
The server, everyone in the workspace. |
|
Fallback, lowest priority. |
Use a browser* layer for view state nobody else cares about, and a *Team layer for something the whole
project should share - a canvas layout that everyone should see the same way, for instance. save with
null clears the value.
Current User, Project, and Workspace
userService.currentUser$, projectService.currentProject$, projectService.currentBranch$, and
workspaceService.currentWorkspace$ are observables of who and where. The project carries its
branches, and the branch says whether it is the primary one - enough
for an extension to warn before writing to production data, or to label what it produces.