dsh-edge 0.2.0-alpha.2 → 0.2.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.
Files changed (43) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +6 -6
  3. package/README.zh.md +6 -6
  4. package/THIRD_PARTY_NOTICES.md +387 -1131
  5. package/dist/assets/index-BNMwCG9c.css +1 -0
  6. package/dist/assets/index-CA9Bpko5.js +96 -0
  7. package/dist/assets/langs/{php-D-uCvoST.js → php-DDkx9sim.js} +2 -2
  8. package/dist/assets/langs/{ruby-DEItuUFs.js → ruby-DCxyoPen.js} +2 -2
  9. package/dist/assets/{vendor-Cjbwl5VI.js → vendor-D22_Mp1f.js} +2 -2
  10. package/dist/index.html +26 -5
  11. package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +166 -27
  12. package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +136 -19
  13. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +51 -12
  14. package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +188 -123
  15. package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +43 -8
  16. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +54 -54
  17. package/dist/plugins/@deepseek-ai/dsh-client-ui-brand-official/client.js +50 -0
  18. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +46 -17
  19. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +805 -486
  20. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +5 -5
  21. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +82 -26
  22. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +7 -7
  23. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +2 -2
  24. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +18 -18
  25. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +96 -82
  26. package/dist/plugins/@deepseek-ai/dsh-client-ui-renderer/client.js +989 -0
  27. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +1177 -67
  28. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +61 -65
  29. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +51 -21
  30. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +12 -12
  31. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +18 -45
  32. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +46 -3
  33. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +102 -62
  34. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +143 -142
  35. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +25 -25
  36. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +261 -86
  37. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +72 -62
  38. package/package.json +22 -22
  39. package/scripts/legal-files.mjs +2 -1
  40. package/worker/direct/index.js +692 -688
  41. package/worker/isolated/index.js +469 -465
  42. package/dist/assets/index-C-1AiF3k.js +0 -112
  43. package/dist/assets/index-CSGf6Qzd.css +0 -1
@@ -5360,6 +5360,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5360
5360
  maxImagesPerMessage: number().int().positive(),
5361
5361
  maxMessageImageBytes: number().int().positive(),
5362
5362
  maxImagePixels: number().int().positive(),
5363
+ maxImageDimension: number().int().positive(),
5363
5364
  mediaTypes: array(string())
5364
5365
  });
5365
5366
  /** session.history response value (projections rides the tail page only). */
@@ -5707,6 +5708,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5707
5708
  provider: string().optional(),
5708
5709
  model: string().optional(),
5709
5710
  attachedSessions: number().int().nonnegative(),
5711
+ home: string(),
5710
5712
  canOpenPath: boolean()
5711
5713
  });
5712
5714
  object({});
@@ -7084,7 +7086,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7084
7086
  toolTurn(67, "grep", "{\"pattern\":\"SEARCH_MAX_LINES\",\"path\":\"packages/client\"}", SEARCH_MATCHES_TEXT);
7085
7087
  toolTurn(68, "glob", "{\"pattern\":\"**/SearchBlock*\",\"path\":\"packages/client\"}", SEARCH_PATHS_TEXT);
7086
7088
  toolTurn(69, "read", `{"file_path":${JSON.stringify(READ_SAMPLE_PATH)},"offset":${READ_SAMPLE_FIRST_LINE}}`, READ_SAMPLE_TEXT);
7087
- toolTurn(70, "web_search", "{\"query\":\"deepseek harness architecture\"}", "Search results for deepseek harness architecture.");
7089
+ toolTurn(70, "web_search", "{\"queries\":[\"deepseek harness architecture\"]}", "Search results for deepseek harness architecture.");
7088
7090
  toolTurn(71, "web_fetch", "{\"url\":\"https://www.deepseek.com/blog/harness-architecture\"}", "# Harness architecture\n\nEverything is a plugin.");
7089
7091
  push({
7090
7092
  type: "turn/start",
@@ -7264,7 +7266,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7264
7266
  };
7265
7267
  case "web_search": return {
7266
7268
  card: "generic",
7267
- title: `Search ${str(args.query)}`,
7269
+ title: `Search ${(Array.isArray(args.queries) ? args.queries.filter((query) => typeof query === "string" && query !== "") : []).join(", ")}`,
7268
7270
  kind: "search",
7269
7271
  rawInput: args
7270
7272
  };
@@ -7357,29 +7359,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7357
7359
  }
7358
7360
  }
7359
7361
  /**
7360
- * Fixture parallel of the plan unit's double-event fold: `command/run`
7361
- * records named `plan` with recorded input set the wanted target (`off` →
7362
- * false, else true); `plan/mode` commits and clears it. `wanted` is exposed
7363
- * for the prompt boundary (the fixture's step/start parallel).
7362
+ * Fixture parallel of the plan unit's lifecycle fold. The paired
7363
+ * `command/done` retains successful plan selections and drops failures;
7364
+ * `plan/mode` commits one. `wanted` is exposed for the prompt boundary (the
7365
+ * fixture's step/start parallel).
7364
7366
  */
7365
7367
  function foldPlan(log) {
7366
7368
  let active = false;
7367
7369
  let wanted = null;
7370
+ let running = null;
7368
7371
  for (const event of log) {
7369
7372
  const item = event;
7370
7373
  if (item.type === "command/run" && item.data?.["name"] === "plan") {
7371
7374
  const args = item.data["args"];
7372
7375
  if (typeof args !== "string") continue;
7373
- wanted = args.trim() !== "off";
7376
+ running = {
7377
+ commandId: item.data["commandId"],
7378
+ wanted: args.trim() !== "off"
7379
+ };
7380
+ } else if (item.type === "command/done" && item.data !== void 0 && running !== null && item.data["commandId"] === running.commandId) {
7381
+ wanted = item.data["kind"] === "success" && running.wanted !== active ? running.wanted : null;
7382
+ running = null;
7374
7383
  } else if (item.type === "plan/mode") {
7375
7384
  active = item.data?.["active"] === true;
7376
7385
  wanted = null;
7377
7386
  }
7378
7387
  }
7388
+ const selected = running?.wanted ?? wanted;
7379
7389
  return {
7380
7390
  active,
7381
- pending: wanted !== null && wanted !== active,
7382
- wanted
7391
+ pending: selected !== null && selected !== active,
7392
+ wanted: selected
7383
7393
  };
7384
7394
  }
7385
7395
  /** The plan projection's wire view over the full log. */
@@ -7619,6 +7629,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7619
7629
  maxImagesPerMessage: 20,
7620
7630
  maxMessageImageBytes: 100 * 1024 * 1024,
7621
7631
  maxImagePixels: 4e7,
7632
+ maxImageDimension: 2e3,
7622
7633
  mediaTypes: [
7623
7634
  "image/png",
7624
7635
  "image/jpeg",
@@ -7985,6 +7996,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7985
7996
  let attachedSessions = options.empty ? 0 : 1;
7986
7997
  const wid = (raw) => raw;
7987
7998
  const fixtureEpoch = (/* @__PURE__ */ new Date(Date.now() - 3e5)).toISOString();
7999
+ const FIXTURE_HOME = "/home/fixture";
7988
8000
  const workspaces = options.empty ? [] : [{
7989
8001
  workspaceId: wid("fx-ws-fixture"),
7990
8002
  path: "/tmp/fixture",
@@ -7996,10 +8008,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7996
8008
  ],
7997
8009
  createdAt: fixtureEpoch,
7998
8010
  updatedAt: fixtureEpoch
8011
+ }, {
8012
+ workspaceId: wid("fx-ws-home"),
8013
+ path: `${FIXTURE_HOME}/Documents/project`,
8014
+ title: "project",
8015
+ sessionIds: [],
8016
+ createdAt: fixtureEpoch,
8017
+ updatedAt: fixtureEpoch
7999
8018
  }];
8000
8019
  let nextWorkspace = 1;
8001
8020
  const archivedSessionIds = [];
8002
- const FIXTURE_HOME = "/home/fixture";
8003
8021
  const directoryTree = new Map([
8004
8022
  ["/", ["home"]],
8005
8023
  ["/home", ["fixture"]],
@@ -8227,7 +8245,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8227
8245
  {
8228
8246
  name: "goal",
8229
8247
  description: "set or view the goal for a long-running task",
8230
- input: { hint: "<objective>" }
8248
+ input: {
8249
+ hint: "<objective>",
8250
+ images: true
8251
+ }
8231
8252
  },
8232
8253
  {
8233
8254
  name: "permission",
@@ -8237,17 +8258,59 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8237
8258
  {
8238
8259
  name: "plan",
8239
8260
  description: "Enter or leave plan mode",
8240
- input: { hint: "[off|message]" }
8261
+ input: {
8262
+ hint: "[off|message]",
8263
+ images: true
8264
+ }
8241
8265
  }
8242
8266
  ]
8243
8267
  };
8244
8268
  },
8245
- execute(id, line) {
8269
+ execute(id, line, images = []) {
8246
8270
  const missing = requireGoalSession(id);
8247
8271
  if (missing !== void 0) return missing;
8248
8272
  const match = /^\/(\S+)((?:\s.*)?)$/.exec(line.trim());
8249
8273
  const name = match?.[1];
8250
8274
  const args = match?.[2] ?? "";
8275
+ if (images.length > 0 && name !== void 0 && [
8276
+ "permission",
8277
+ "goal",
8278
+ "compact",
8279
+ "echo",
8280
+ "plan"
8281
+ ].includes(name)) {
8282
+ const rejection = name !== "goal" && name !== "plan" ? `/${name} does not accept image attachments` : name === "goal" && args.trim() === "" ? "Image attachments only accompany a goal objective: /goal <objective> or /goal edit <objective>." : name === "plan" && args.trim() === "off" ? "Image attachments cannot accompany /plan off." : void 0;
8283
+ if (rejection !== void 0) {
8284
+ const commandId = `fx-cmd-${logOf(id).length}`;
8285
+ append(id, {
8286
+ type: "command/run",
8287
+ data: {
8288
+ commandId,
8289
+ name,
8290
+ args,
8291
+ source: { kind: "user" }
8292
+ }
8293
+ });
8294
+ const result = {
8295
+ kind: "error",
8296
+ text: rejection
8297
+ };
8298
+ append(id, {
8299
+ type: "command/done",
8300
+ data: {
8301
+ commandId,
8302
+ ...result
8303
+ }
8304
+ });
8305
+ return {
8306
+ ok: true,
8307
+ value: {
8308
+ commandId,
8309
+ result
8310
+ }
8311
+ };
8312
+ }
8313
+ }
8251
8314
  if (name === "permission") {
8252
8315
  const preset = args.trim();
8253
8316
  const commandId = `fx-cmd-${logOf(id).length}`;
@@ -8409,6 +8472,50 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8409
8472
  activation: projection.goal.phase === "active" ? "armed" : "disarmed"
8410
8473
  });
8411
8474
  /** Canonical fixture implementation of the generated Goal Remote contract. */
8475
+ /** Canonical fixture implementation of the generated reference-discovery Remote contracts. */
8476
+ const referenceRemotes = {
8477
+ files(id, query) {
8478
+ const missing = requireGoalSession(id);
8479
+ if (missing !== void 0) return missing;
8480
+ const needle = query.toLocaleLowerCase();
8481
+ return {
8482
+ ok: true,
8483
+ value: [
8484
+ {
8485
+ path: "notes",
8486
+ kind: "directory"
8487
+ },
8488
+ {
8489
+ path: "README.md",
8490
+ kind: "file"
8491
+ },
8492
+ {
8493
+ path: "notes/demo.txt",
8494
+ kind: "file"
8495
+ }
8496
+ ].filter((item) => item.path.toLocaleLowerCase().includes(needle))
8497
+ };
8498
+ },
8499
+ sessions(id, query) {
8500
+ const missing = requireGoalSession(id);
8501
+ if (missing !== void 0) return missing;
8502
+ const needle = query.toLocaleLowerCase();
8503
+ return {
8504
+ ok: true,
8505
+ value: sessions.filter((item) => item.sessionId !== id).filter((item) => String(item.sessionId).toLocaleLowerCase().includes(needle) || item.cwd?.toLocaleLowerCase().includes(needle) === true).map((item) => {
8506
+ const label = item.sessionId === sid("fx-beta") ? "Fixture child session" : String(item.sessionId);
8507
+ const encoded = btoa(JSON.stringify(item.sessionId)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
8508
+ return {
8509
+ sessionId: item.sessionId,
8510
+ label,
8511
+ ...item.cwd === void 0 ? {} : { cwd: item.cwd },
8512
+ createdAt: item.updatedAt,
8513
+ mention: `@[${label}](dsh-session:${encoded})`
8514
+ };
8515
+ })
8516
+ };
8517
+ }
8518
+ };
8412
8519
  const goalRemotes = {
8413
8520
  create(id, request) {
8414
8521
  const missing = requireGoalSession(id);
@@ -9204,11 +9311,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9204
9311
  message: `no session ${id}`,
9205
9312
  details: { sessionId: id }
9206
9313
  });
9207
- if (options.rejectPrompt) return err(request, {
9208
- code: "agent-busy",
9209
- message: "fixture: prompt rejected before acceptance",
9210
- details: { reason: "fixture-prompt-rejection" }
9211
- });
9314
+ if (options.rejectPrompt) {
9315
+ if (content.some((block) => block.type === "image")) return err(request, {
9316
+ code: "attachment-error",
9317
+ message: "fixture: image side exceeds the deployment limit",
9318
+ details: { reason: "IMAGE_DIMENSION_TOO_LARGE" }
9319
+ });
9320
+ return err(request, {
9321
+ code: "agent-busy",
9322
+ message: "fixture: prompt rejected before acceptance",
9323
+ details: { reason: "fixture-prompt-rejection" }
9324
+ });
9325
+ }
9212
9326
  summary.updatedAt = Date.now();
9213
9327
  summary.blank = false;
9214
9328
  const userText = content.map((b) => b.type === "text" ? b.text : "").join("");
@@ -9319,6 +9433,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9319
9433
  version: "0.0.0-fixture",
9320
9434
  cwd: "/tmp/fixture",
9321
9435
  attachedSessions,
9436
+ home: FIXTURE_HOME,
9322
9437
  canOpenPath: true
9323
9438
  }),
9324
9439
  pickDirectory: (request) => ok(request, { path: `${FIXTURE_HOME}/Documents/project` }),
@@ -9831,7 +9946,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9831
9946
  const sessionId = args.agentId;
9832
9947
  switch (endpoint) {
9833
9948
  case "commands/list": return Promise.resolve(commandRemotes.list(sessionId));
9834
- case "commands/execute": return Promise.resolve(commandRemotes.execute(sessionId, args.line));
9949
+ case "commands/execute": return Promise.resolve(commandRemotes.execute(sessionId, args.line, args.images ?? []));
9950
+ case "fileReferences/list": return Promise.resolve(referenceRemotes.files(sessionId, args.query ?? ""));
9951
+ case "sessionReferenceResolver/candidates": return Promise.resolve(referenceRemotes.sessions(sessionId, args.query ?? ""));
9835
9952
  case "goals/create": return Promise.resolve(goalRemotes.create(sessionId, {
9836
9953
  objective: args.request?.objective,
9837
9954
  ...args.request?.maxGoalRounds === void 0 ? {} : { maxGoalRounds: args.request.maxGoalRounds }
@@ -886,11 +886,11 @@ window.__ModuleLoader__.load({
886
886
  document.head.appendChild(tag);
887
887
  }
888
888
  var LanguageRow_module_css_default = {
889
- "selector": "hVGvvW_selector",
890
- "rowText": "hVGvvW_rowText",
891
- "title": "hVGvvW_title",
889
+ "chevron": "hVGvvW_chevron",
892
890
  "row": "hVGvvW_row",
893
- "chevron": "hVGvvW_chevron"
891
+ "rowText": "hVGvvW_rowText",
892
+ "selector": "hVGvvW_selector",
893
+ "title": "hVGvvW_title"
894
894
  };
895
895
  //#endregion
896
896
  //#region lib/types/client/LanguageRow.js
@@ -975,8 +975,16 @@ window.__ModuleLoader__.load({
975
975
  }
976
976
  //#endregion
977
977
  //#region lib/types/client/index.js
978
- /** Fallback locale consulted after the active locale misses (also the last-resort initial locale). */
979
- const FALLBACK_LOCALE = "zh";
978
+ /**
979
+ * English is both the locale the UI opens in when the browser names no shipped
980
+ * language (and for non-browser runs), and the dictionary consulted after the
981
+ * active locale misses a key. One constant serves both because the shipped
982
+ * `zh`/`en` dictionaries carry identical key sets, so neither direction can
983
+ * leave a key unresolved; the residual case points at English rather than
984
+ * zh because a browser naming neither shipped language is the reader least
985
+ * likely to read Chinese.
986
+ */
987
+ const FALLBACK_LOCALE = "en";
980
988
  /** Shared namespace for shell-level texts. */
981
989
  const COMMON_NS = "common";
982
990
  /** Namespace owning this feature's settings-row copy. */
@@ -990,9 +998,31 @@ window.__ModuleLoader__.load({
990
998
  label: "English"
991
999
  }]);
992
1000
  /**
1001
+ * `<html lang>` tag per shipped locale. The locale id is the app's own
1002
+ * vocabulary (primary subtag); the document attribute wants a BCP 47 tag,
1003
+ * which assistive technology and browser features (pronunciation rules,
1004
+ * translation offers, font fallback, spell check) read to pick their own
1005
+ * behavior. `zh` alone leaves the script ambiguous, so the shipped Chinese
1006
+ * copy names the variant it actually is.
1007
+ */
1008
+ const DOCUMENT_LANGUAGE = {
1009
+ zh: "zh-CN",
1010
+ en: "en"
1011
+ };
1012
+ /**
1013
+ * Point `<html lang>` at the active locale. Called on every locale change,
1014
+ * so the attribute tracks the UI instead of standing at whatever the served
1015
+ * markup happened to declare.
1016
+ * @param active - the active locale id.
1017
+ */
1018
+ function syncDocumentLanguage(active) {
1019
+ if (typeof document === "undefined") return;
1020
+ document.documentElement.lang = DOCUMENT_LANGUAGE[active];
1021
+ }
1022
+ /**
993
1023
  * Dictionary registry plus locale preference. Lookup chain per key: the
994
- * entry's namespace in the active locale -> that namespace's zh fallback ->
995
- * the shared common namespace (active, then zh) -> the key itself (missing
1024
+ * entry's namespace in the active locale -> that namespace's en fallback ->
1025
+ * the shared common namespace (active, then en) -> the key itself (missing
996
1026
  * text stays visible, fail loud in the UI rather than blank). Reads go
997
1027
  * through {@link getLocale}; writes only through {@link setLocale};
998
1028
  * continuous sync through the `locale/change` event, or through the
@@ -1060,13 +1090,20 @@ window.__ModuleLoader__.load({
1060
1090
  }
1061
1091
  /**
1062
1092
  * Switch the active locale — the only user preference write entry.
1093
+ *
1094
+ * The durable write happens even when the id already matches the active
1095
+ * locale, because the active value may be a provisional browser-derived or
1096
+ * fallback resolution that nothing has stored yet. Picking the language
1097
+ * already on screen is still an explicit choice, and it must survive a
1098
+ * different browser sharing the same DSH home. Only the render notification
1099
+ * is conditional: republishing an unchanged locale would churn every
1100
+ * subscriber for nothing.
1063
1101
  * @param id - a registered locale id; unknown ids throw.
1064
1102
  */
1065
1103
  setLocale(id) {
1066
1104
  const match = this.snapshot.locales.find((l) => l.id === id);
1067
1105
  if (match === void 0) throw new Error(`locale "${id}" is not registered`);
1068
- if (this.snapshot.active === match.id) return;
1069
- this.publish(match.id, true);
1106
+ if (this.snapshot.active !== match.id) this.publish(match.id, true);
1070
1107
  this.host?.set(LOCALE_PREFERENCE_FIELD, match.id);
1071
1108
  }
1072
1109
  /**
@@ -1120,7 +1157,7 @@ window.__ModuleLoader__.load({
1120
1157
  }
1121
1158
  lookup(ns, key) {
1122
1159
  const locales = this.dicts.get(ns);
1123
- return locales?.get(this.snapshot.active)?.[key] ?? locales?.get("zh")?.[key];
1160
+ return locales?.get(this.snapshot.active)?.[key] ?? locales?.get("en")?.[key];
1124
1161
  }
1125
1162
  /**
1126
1163
  * Advance the snapshot revision and notify LocaleFace subscribers (render
@@ -1148,7 +1185,7 @@ window.__ModuleLoader__.load({
1148
1185
  * Host preference may replace this provisional value after plugin activation.
1149
1186
  */
1150
1187
  function resolveInitialLocale() {
1151
- return detectBrowserLocale() ?? "zh";
1188
+ return detectBrowserLocale() ?? "en";
1152
1189
  }
1153
1190
  /**
1154
1191
  * The first shipped locale the browser asks for, matched on the primary
@@ -1195,12 +1232,14 @@ window.__ModuleLoader__.load({
1195
1232
  const store = createLanguageRowStore();
1196
1233
  let bound;
1197
1234
  const sync = (snapshot) => {
1235
+ syncDocumentLanguage(snapshot.active);
1198
1236
  bound?.sync(snapshot.active, snapshot.locales.map((l) => ({
1199
1237
  id: l.id,
1200
1238
  label: l.label
1201
1239
  })), snapshot.revision);
1202
1240
  };
1203
1241
  ctx.on("locale/change", sync);
1242
+ syncDocumentLanguage(locale.getLocale().active);
1204
1243
  const injected = (actions) => {
1205
1244
  bound = actions;
1206
1245
  sync(locale.getLocale());