@qodalis/cli 2.2.0 → 2.4.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/public-api.js CHANGED
@@ -519,7 +519,7 @@ var CliKeyValueStore_TOKEN = cliCore.createCliToken("cli-key-value-store");
519
519
  var SyntaxHighlighterRegistry_TOKEN = cliCore.createCliToken("syntax-highlighter-registry");
520
520
 
521
521
  // src/lib/version.ts
522
- var LIBRARY_VERSION = "2.2.0";
522
+ var LIBRARY_VERSION = "2.4.0";
523
523
  var API_VERSION = 2;
524
524
  var ANONYMOUS = "\0anonymous";
525
525
  function aliasOwner(context2) {
@@ -11188,7 +11188,78 @@ var CliServicesCommandProcessor = class {
11188
11188
  );
11189
11189
  }
11190
11190
  };
11191
+ var CLI_EVENT_HISTORY_SIZE = 200;
11192
+ function matchesEventPattern(type, pattern) {
11193
+ if (pattern === "*") return true;
11194
+ if (pattern.endsWith(":*")) {
11195
+ return type.startsWith(pattern.slice(0, -1));
11196
+ }
11197
+ return type === pattern;
11198
+ }
11199
+ var CliEventBus = class {
11200
+ constructor(onHandlerError = (error) => console.error("[CLI] Event handler failed:", error), historySize = CLI_EVENT_HISTORY_SIZE) {
11201
+ this.onHandlerError = onHandlerError;
11202
+ this.historySize = historySize;
11203
+ this.subject = new rxjs.Subject();
11204
+ this.recent = [];
11205
+ this.disposed = false;
11206
+ }
11207
+ emit(type, data, options) {
11208
+ if (this.disposed) return;
11209
+ const event = {
11210
+ type,
11211
+ data,
11212
+ source: options?.source,
11213
+ timestamp: Date.now()
11214
+ };
11215
+ this.recent.push(event);
11216
+ if (this.recent.length > this.historySize) {
11217
+ this.recent.shift();
11218
+ }
11219
+ this.subject.next(event);
11220
+ }
11221
+ on(pattern, handler) {
11222
+ const subscription = this.events$(pattern).subscribe((event) => {
11223
+ try {
11224
+ handler(event);
11225
+ } catch (error) {
11226
+ this.onHandlerError(error, event);
11227
+ }
11228
+ });
11229
+ return () => subscription.unsubscribe();
11230
+ }
11231
+ once(pattern, handler) {
11232
+ let done = false;
11233
+ const unsubscribe = this.on(pattern, (event) => {
11234
+ if (done) return;
11235
+ done = true;
11236
+ unsubscribe();
11237
+ handler(event);
11238
+ });
11239
+ return unsubscribe;
11240
+ }
11241
+ events$(pattern = "*") {
11242
+ return this.subject.asObservable().pipe(operators.filter((event) => matchesEventPattern(event.type, pattern)));
11243
+ }
11244
+ history(pattern = "*", limit) {
11245
+ const matching = this.recent.filter(
11246
+ (event) => matchesEventPattern(event.type, pattern)
11247
+ );
11248
+ return limit !== void 0 ? matching.slice(-limit) : matching;
11249
+ }
11250
+ /** Stop delivering events and complete all observables. */
11251
+ dispose() {
11252
+ if (this.disposed) return;
11253
+ this.disposed = true;
11254
+ this.subject.complete();
11255
+ this.recent.length = 0;
11256
+ }
11257
+ };
11258
+
11259
+ // src/lib/processors/system/cli-events-command-processor.ts
11191
11260
  var DATA_PREVIEW_LENGTH = 120;
11261
+ var SERVER_EVENT = "server:event";
11262
+ var SERVER_ENVELOPE_FIELDS = /* @__PURE__ */ new Set(["type", "timestamp"]);
11192
11263
  var RESERVED_NAMESPACES = [
11193
11264
  "boot",
11194
11265
  "command",
@@ -11200,26 +11271,83 @@ var RESERVED_NAMESPACES = [
11200
11271
  "session",
11201
11272
  "user"
11202
11273
  ];
11274
+ function serverEventOf(event) {
11275
+ return event.type === SERVER_EVENT ? event.data : void 0;
11276
+ }
11277
+ function matchesFilter(event, filter2) {
11278
+ const remote = serverEventOf(event);
11279
+ if (!remote) {
11280
+ return !filter2.server && matchesEventPattern(event.type, filter2.pattern);
11281
+ }
11282
+ if (filter2.local) return false;
11283
+ if (filter2.server && remote.server.toLowerCase() !== filter2.server.toLowerCase()) return false;
11284
+ return matchesEventPattern(remote.type, filter2.pattern) || matchesEventPattern(SERVER_EVENT, filter2.pattern);
11285
+ }
11203
11286
  function canSee(event, context2) {
11204
11287
  return !event.type.startsWith("user:") || cliCore.cliAuthorize(context2, cliCore.CliUsersPermissions.AuditRead);
11205
11288
  }
11289
+ function formatServerEventFields(data) {
11290
+ if (!data || typeof data !== "object") return "";
11291
+ const parts = [];
11292
+ for (const [key, value] of Object.entries(data)) {
11293
+ if (SERVER_ENVELOPE_FIELDS.has(key) || value === void 0 || value === null) continue;
11294
+ const text = typeof value === "string" ? value : JSON.stringify(value);
11295
+ parts.push(`${key}=${/\s/.test(text) ? JSON.stringify(text) : text}`);
11296
+ }
11297
+ return shorten(parts.join(" "));
11298
+ }
11299
+ function shorten(text) {
11300
+ return text.length > DATA_PREVIEW_LENGTH ? `${text.slice(0, DATA_PREVIEW_LENGTH - 1)}\u2026` : text;
11301
+ }
11206
11302
  function formatEvent(event, context2) {
11207
11303
  const { writer } = context2;
11208
- const time = new Date(event.timestamp).toISOString().slice(11, 23);
11209
- const source = event.source ? ` ${writer.wrapInColor(`[${event.source}]`, cliCore.CliForegroundColor.Blue)}` : "";
11304
+ const remote = serverEventOf(event);
11305
+ let time = event.timestamp;
11306
+ let type = event.type;
11307
+ let source = event.source;
11210
11308
  let data = "";
11211
- if (event.data !== void 0) {
11309
+ if (remote) {
11310
+ const sent = remote.data?.timestamp;
11311
+ if (typeof sent === "string" && !isNaN(Date.parse(sent))) time = sent;
11312
+ type = remote.type;
11313
+ source = remote.server;
11314
+ data = formatServerEventFields(remote.data);
11315
+ } else if (event.data !== void 0) {
11212
11316
  try {
11213
11317
  data = JSON.stringify(event.data) ?? "";
11214
11318
  } catch {
11215
11319
  data = String(event.data);
11216
11320
  }
11217
- if (data.length > DATA_PREVIEW_LENGTH) {
11218
- data = `${data.slice(0, DATA_PREVIEW_LENGTH - 1)}\u2026`;
11219
- }
11321
+ data = shorten(data);
11220
11322
  }
11221
- return `${writer.wrapInColor(time, cliCore.CliForegroundColor.Yellow)} ${writer.wrapInColor(event.type, cliCore.CliForegroundColor.Cyan)}${source}${data ? ` ${data}` : ""}`;
11323
+ const when = new Date(time).toISOString().slice(11, 23);
11324
+ const from = source ? ` ${writer.wrapInColor(`[${source}]`, remote ? cliCore.CliForegroundColor.Magenta : cliCore.CliForegroundColor.Blue)}` : "";
11325
+ return `${writer.wrapInColor(when, cliCore.CliForegroundColor.Yellow)} ${writer.wrapInColor(type, cliCore.CliForegroundColor.Cyan)}${from}${data ? ` ${data}` : ""}`;
11326
+ }
11327
+ function filterOf(command2) {
11328
+ const server = command2.args["server"] ?? command2.args["s"];
11329
+ return {
11330
+ pattern: cliCore.joinShellWords(command2.value ?? "") || "*",
11331
+ server: server === void 0 || server === true ? void 0 : String(server),
11332
+ local: !!(command2.args["local"] || command2.args["l"])
11333
+ };
11222
11334
  }
11335
+ var FILTER_PARAMETERS = [
11336
+ {
11337
+ name: "server",
11338
+ aliases: ["s"],
11339
+ type: "string",
11340
+ description: "Only the events of this server",
11341
+ required: false
11342
+ },
11343
+ {
11344
+ name: "local",
11345
+ aliases: ["l"],
11346
+ type: "boolean",
11347
+ description: "Only the terminal's own events",
11348
+ required: false
11349
+ }
11350
+ ];
11223
11351
  function busOf(context2) {
11224
11352
  if (!context2.events) {
11225
11353
  context2.writer.writeError(
@@ -11231,7 +11359,7 @@ function busOf(context2) {
11231
11359
  var CliEventsCommandProcessor = class {
11232
11360
  constructor() {
11233
11361
  this.command = "events";
11234
- this.description = "Watch, list and emit events on the event bus";
11362
+ this.description = "Watch, list and emit events of the terminal and the connected servers";
11235
11363
  this.author = cliCore.DefaultLibraryAuthor;
11236
11364
  this.version = "1.0.0";
11237
11365
  this.metadata = {
@@ -11244,16 +11372,17 @@ var CliEventsCommandProcessor = class {
11244
11372
  command: "watch",
11245
11373
  description: "Print events as they happen (Ctrl+C to stop)",
11246
11374
  acceptsRawInput: true,
11375
+ parameters: FILTER_PARAMETERS,
11247
11376
  processCommand: async (command2, context2) => {
11248
11377
  const bus = busOf(context2);
11249
11378
  if (!bus) return;
11250
- const pattern = cliCore.joinShellWords(command2.value ?? "") || "*";
11379
+ const filter2 = filterOf(command2);
11251
11380
  context2.writer.writeInfo(
11252
- `Watching ${context2.writer.wrapInColor(pattern, cliCore.CliForegroundColor.Cyan)} \u2014 press Ctrl+C to stop`
11381
+ `Watching ${context2.writer.wrapInColor(filter2.pattern, cliCore.CliForegroundColor.Cyan)} \u2014 press Ctrl+C to stop`
11253
11382
  );
11254
11383
  await new Promise((resolve) => {
11255
- const off = bus.on(pattern, (event) => {
11256
- if (canSee(event, context2)) {
11384
+ const off = bus.on("*", (event) => {
11385
+ if (matchesFilter(event, filter2) && canSee(event, context2)) {
11257
11386
  context2.writer.writeln(formatEvent(event, context2));
11258
11387
  }
11259
11388
  });
@@ -11279,6 +11408,12 @@ var CliEventsCommandProcessor = class {
11279
11408
  context2.writer.writeln(
11280
11409
  ` events watch boot:completed ${t.t("cli.events.watch.help.one_type", "One type")}`
11281
11410
  );
11411
+ context2.writer.writeln(
11412
+ ` events watch job:* -s node ${t.t("cli.events.watch.help.server_jobs", "Job events of the server node")}`
11413
+ );
11414
+ context2.writer.writeln(
11415
+ ` events watch --local ${t.t("cli.events.watch.help.local_only", "Only the terminal's own events")}`
11416
+ );
11282
11417
  }
11283
11418
  },
11284
11419
  {
@@ -11287,6 +11422,7 @@ var CliEventsCommandProcessor = class {
11287
11422
  description: "Show recent events (kept in memory, not persisted)",
11288
11423
  acceptsRawInput: true,
11289
11424
  parameters: [
11425
+ ...FILTER_PARAMETERS,
11290
11426
  {
11291
11427
  name: "limit",
11292
11428
  aliases: ["n"],
@@ -11298,13 +11434,13 @@ var CliEventsCommandProcessor = class {
11298
11434
  processCommand: async (command2, context2) => {
11299
11435
  const bus = busOf(context2);
11300
11436
  if (!bus) return;
11301
- const pattern = cliCore.joinShellWords(command2.value ?? "") || "*";
11437
+ const filter2 = filterOf(command2);
11302
11438
  const limit = Number(
11303
11439
  command2.args["limit"] ?? command2.args["n"] ?? 20
11304
11440
  );
11305
- const events = bus.history(pattern).filter((event) => canSee(event, context2)).slice(-(limit > 0 ? limit : 20));
11441
+ const events = bus.history("*").filter((event) => matchesFilter(event, filter2) && canSee(event, context2)).slice(-(limit > 0 ? limit : 20));
11306
11442
  if (events.length === 0) {
11307
- context2.writer.writeInfo(`No events matching ${pattern}`);
11443
+ context2.writer.writeInfo(`No events matching ${filter2.pattern}`);
11308
11444
  return;
11309
11445
  }
11310
11446
  for (const event of events) {
@@ -11323,6 +11459,9 @@ var CliEventsCommandProcessor = class {
11323
11459
  context2.writer.writeln(
11324
11460
  ` events log server:* -n 50 ${t.t("cli.events.log.help.last_50_server", "Last 50 server events")}`
11325
11461
  );
11462
+ context2.writer.writeln(
11463
+ ` events log job:failed ${t.t("cli.events.log.help.failed_jobs", "Recent failed job runs, on every server")}`
11464
+ );
11326
11465
  }
11327
11466
  },
11328
11467
  {
@@ -11394,6 +11533,9 @@ var CliEventsCommandProcessor = class {
11394
11533
  writer.writeln(
11395
11534
  t.t("cli.events.help.patterns", "Patterns: an exact type (package:installed), a namespace (package:*) or * for all.")
11396
11535
  );
11536
+ writer.writeln(
11537
+ t.t("cli.events.help.servers", "Events of the connected servers (job runs, user, token and file changes, ...) are shown with the server name; each server sends only the events you may see. Filter with --server <name>, or --local for the terminal's own events.")
11538
+ );
11397
11539
  writer.writeln(
11398
11540
  t.t("cli.events.help.in_memory", "Events are kept in memory for this terminal only; nothing is persisted.")
11399
11541
  );
@@ -12165,6 +12307,53 @@ var themeInfos = {
12165
12307
  ...cliCore.DefaultThemeInfos
12166
12308
  };
12167
12309
 
12310
+ // src/lib/processors/theme/terminal-theme.ts
12311
+ var records = /* @__PURE__ */ new WeakMap();
12312
+ function recordOf(terminal) {
12313
+ let record = records.get(terminal);
12314
+ if (!record) {
12315
+ record = { base: terminal.options.theme ?? {}, userChosen: false };
12316
+ records.set(terminal, record);
12317
+ }
12318
+ return record;
12319
+ }
12320
+ var CLI_TERMINAL_THEME_EVENT = "qcli-terminal-theme";
12321
+ function paintTerminalBackground(terminal) {
12322
+ const container = terminal.element?.closest(".terminal-container");
12323
+ if (!container) return;
12324
+ const theme = terminal.options.theme ?? {};
12325
+ container.style.background = theme.background ?? "";
12326
+ if (typeof CustomEvent === "function") {
12327
+ container.dispatchEvent(new CustomEvent(CLI_TERMINAL_THEME_EVENT, { bubbles: true, detail: theme }));
12328
+ }
12329
+ }
12330
+ function setHostTerminalTheme(terminal, theme) {
12331
+ const record = recordOf(terminal);
12332
+ record.base = theme;
12333
+ if (!record.userChosen) {
12334
+ terminal.options.theme = theme;
12335
+ paintTerminalBackground(terminal);
12336
+ }
12337
+ }
12338
+ function applyUserTerminalTheme(terminal, theme) {
12339
+ recordOf(terminal).userChosen = true;
12340
+ terminal.options.theme = theme;
12341
+ paintTerminalBackground(terminal);
12342
+ }
12343
+ function resetTerminalTheme(terminal) {
12344
+ const record = recordOf(terminal);
12345
+ record.userChosen = false;
12346
+ terminal.options.theme = { ...record.base };
12347
+ paintTerminalBackground(terminal);
12348
+ }
12349
+ function previewTerminalTheme(terminal, theme) {
12350
+ terminal.options.theme = theme;
12351
+ paintTerminalBackground(terminal);
12352
+ }
12353
+ function initTerminalTheme(terminal) {
12354
+ recordOf(terminal);
12355
+ }
12356
+
12168
12357
  // src/lib/processors/theme/cli-theme-command-processor.ts
12169
12358
  function hexToRgb(hex) {
12170
12359
  const m = /^#?([0-9A-Fa-f]{6})$/.exec(hex);
@@ -12365,15 +12554,19 @@ var CliThemeCommandProcessor = class {
12365
12554
  {
12366
12555
  onChange: (value) => {
12367
12556
  if (themes[value]) {
12368
- context2.terminal.options.theme = themes[value];
12369
- this.applyStyles(context2);
12557
+ previewTerminalTheme(
12558
+ context2.terminal,
12559
+ themes[value]
12560
+ );
12370
12561
  }
12371
12562
  }
12372
12563
  }
12373
12564
  );
12374
12565
  if (!selected) {
12375
- context2.terminal.options.theme = originalTheme;
12376
- this.applyStyles(context2);
12566
+ previewTerminalTheme(
12567
+ context2.terminal,
12568
+ originalTheme
12569
+ );
12377
12570
  context2.writer.writeInfo(
12378
12571
  "Theme selection cancelled"
12379
12572
  );
@@ -12387,13 +12580,12 @@ var CliThemeCommandProcessor = class {
12387
12580
  );
12388
12581
  return;
12389
12582
  }
12390
- context2.terminal.options.theme = themes[themeName];
12583
+ applyUserTerminalTheme(context2.terminal, themes[themeName]);
12391
12584
  context2.state.updateState({
12392
12585
  selectedTheme: themeName,
12393
12586
  customOptions: null
12394
12587
  });
12395
12588
  await context2.state.persist();
12396
- this.applyStyles(context2);
12397
12589
  context2.writer.writeSuccess(`Theme "${themeName}" applied`);
12398
12590
  }
12399
12591
  },
@@ -12429,10 +12621,10 @@ var CliThemeCommandProcessor = class {
12429
12621
  );
12430
12622
  return;
12431
12623
  }
12432
- context2.terminal.options.theme = {
12624
+ applyUserTerminalTheme(context2.terminal, {
12433
12625
  ...context2.terminal.options.theme,
12434
12626
  [key]: value
12435
- };
12627
+ });
12436
12628
  context2.state.updateState({
12437
12629
  selectedTheme: null,
12438
12630
  customOptions: context2.terminal.options.theme
@@ -12441,7 +12633,6 @@ var CliThemeCommandProcessor = class {
12441
12633
  context2.writer.writeSuccess(
12442
12634
  `Set ${key} to ${swatch} ${value}`
12443
12635
  );
12444
- this.applyStyles(context2);
12445
12636
  if (command2.args["save"]) {
12446
12637
  await this.saveTheme(context2);
12447
12638
  }
@@ -12467,10 +12658,9 @@ var CliThemeCommandProcessor = class {
12467
12658
  command: "reset",
12468
12659
  description: "Reset the theme to the default",
12469
12660
  processCommand: async (_, context2) => {
12470
- context2.terminal.options.theme = { ...this.defaultTheme };
12661
+ resetTerminalTheme(context2.terminal);
12471
12662
  context2.state.reset();
12472
12663
  await context2.state.persist();
12473
- this.applyStyles(context2);
12474
12664
  context2.writer.writeSuccess("Theme reset to default");
12475
12665
  }
12476
12666
  },
@@ -12528,13 +12718,12 @@ var CliThemeCommandProcessor = class {
12528
12718
  );
12529
12719
  }
12530
12720
  const [name, info] = candidates[Math.floor(Math.random() * candidates.length)];
12531
- context2.terminal.options.theme = info.theme;
12721
+ applyUserTerminalTheme(context2.terminal, info.theme);
12532
12722
  context2.state.updateState({
12533
12723
  selectedTheme: name,
12534
12724
  customOptions: null
12535
12725
  });
12536
12726
  await context2.state.persist();
12537
- this.applyStyles(context2);
12538
12727
  context2.writer.writeSuccess(
12539
12728
  `Random theme "${name}" applied`
12540
12729
  );
@@ -12585,13 +12774,12 @@ var CliThemeCommandProcessor = class {
12585
12774
  );
12586
12775
  return;
12587
12776
  }
12588
- context2.terminal.options.theme = imported;
12777
+ applyUserTerminalTheme(context2.terminal, imported);
12589
12778
  context2.state.updateState({
12590
12779
  selectedTheme: null,
12591
12780
  customOptions: imported
12592
12781
  });
12593
12782
  await context2.state.persist();
12594
- this.applyStyles(context2);
12595
12783
  context2.writer.writeSuccess(
12596
12784
  "Custom theme imported and applied"
12597
12785
  );
@@ -12600,8 +12788,7 @@ var CliThemeCommandProcessor = class {
12600
12788
  ];
12601
12789
  }
12602
12790
  async initialize(context2) {
12603
- this.defaultTheme = context2.terminal.options.theme;
12604
- if (context2.options?.theme !== void 0) return;
12791
+ initTerminalTheme(context2.terminal);
12605
12792
  this.applySavedTheme(context2, false);
12606
12793
  context2.state.select(
12607
12794
  (state) => JSON.stringify([
@@ -12613,17 +12800,14 @@ var CliThemeCommandProcessor = class {
12613
12800
  /** Apply the saved theme (or the default one when `reset` and none is saved). */
12614
12801
  applySavedTheme(context2, reset) {
12615
12802
  const state = context2.state.getState();
12616
- if (state.selectedTheme && state.selectedTheme !== "default") {
12617
- const theme = themes[state.selectedTheme];
12618
- if (theme) {
12619
- context2.terminal.options.theme = theme;
12620
- }
12621
- } else if (state.customOptions) {
12622
- context2.terminal.options.theme = state.customOptions;
12803
+ const saved = state.selectedTheme && state.selectedTheme !== "default" ? themes[state.selectedTheme] : state.customOptions;
12804
+ if (saved) {
12805
+ applyUserTerminalTheme(context2.terminal, saved);
12623
12806
  } else if (reset) {
12624
- context2.terminal.options.theme = { ...this.defaultTheme };
12807
+ resetTerminalTheme(context2.terminal);
12808
+ } else {
12809
+ paintTerminalBackground(context2.terminal);
12625
12810
  }
12626
- this.applyStyles(context2);
12627
12811
  }
12628
12812
  async processCommand(command2, context2) {
12629
12813
  context2.executor.showHelp(command2, context2);
@@ -12702,12 +12886,6 @@ var CliThemeCommandProcessor = class {
12702
12886
  );
12703
12887
  }
12704
12888
  }
12705
- applyStyles(context2) {
12706
- const parents = document.getElementsByClassName("terminal-container");
12707
- for (const parent of Array.from(parents)) {
12708
- parent.style.background = context2.terminal.options.theme?.background || this.defaultTheme.background;
12709
- }
12710
- }
12711
12889
  async saveTheme(context2) {
12712
12890
  await context2.state.persist();
12713
12891
  context2.writer.writeSuccess("Theme saved");
@@ -13208,6 +13386,7 @@ var CliEnvCommandProcessor = class {
13208
13386
  var MIN_FONT_SIZE = 8;
13209
13387
  var MAX_FONT_SIZE = 40;
13210
13388
  var STEP = 2;
13389
+ var hostFontSizes = /* @__PURE__ */ new WeakMap();
13211
13390
  var CliFontSizeCommandProcessor = class {
13212
13391
  constructor() {
13213
13392
  this.command = "font-size";
@@ -13262,14 +13441,16 @@ var CliFontSizeCommandProcessor = class {
13262
13441
  ];
13263
13442
  }
13264
13443
  async initialize(context2) {
13265
- this.hostFontSize = context2.terminal?.options?.fontSize;
13444
+ if (context2.terminal) {
13445
+ hostFontSizes.set(context2.terminal, context2.terminal.options?.fontSize);
13446
+ }
13266
13447
  const state = context2.state.getState();
13267
13448
  if (state?.fontSize != null && context2.terminal?.options) {
13268
13449
  context2.terminal.options.fontSize = state.fontSize;
13269
13450
  }
13270
13451
  context2.state.select((s) => s.fontSize ?? null).pipe(operators.skip(1)).subscribe((fontSize) => {
13271
13452
  if (!context2.terminal?.options) return;
13272
- context2.terminal.options.fontSize = fontSize ?? this.hostFontSize ?? context2.terminal.options.fontSize;
13453
+ context2.terminal.options.fontSize = fontSize ?? this.getDefault(context2);
13273
13454
  });
13274
13455
  }
13275
13456
  async processCommand(_, context2) {
@@ -13287,7 +13468,7 @@ var CliFontSizeCommandProcessor = class {
13287
13468
  ` ${context2.writer.wrapInColor("font-size set <n>", cliCore.CliForegroundColor.Cyan)} Set to exact size`
13288
13469
  );
13289
13470
  context2.writer.writeln(
13290
- ` ${context2.writer.wrapInColor("font-size reset", cliCore.CliForegroundColor.Cyan)} Reset to default (${this.getDefault()}px)`
13471
+ ` ${context2.writer.wrapInColor("font-size reset", cliCore.CliForegroundColor.Cyan)} Reset to default (${this.getDefault(context2)}px)`
13291
13472
  );
13292
13473
  }
13293
13474
  writeDescription(context2) {
@@ -13320,13 +13501,13 @@ var CliFontSizeCommandProcessor = class {
13320
13501
  return fromTerminal;
13321
13502
  }
13322
13503
  const state = context2.state.getState();
13323
- return state?.fontSize ?? this.getDefault();
13504
+ return state?.fontSize ?? this.getDefault(context2);
13324
13505
  }
13325
- getDefault() {
13326
- return this.hostFontSize ?? defaultFontSize();
13506
+ getDefault(context2) {
13507
+ return (context2.terminal && hostFontSizes.get(context2.terminal)) ?? defaultFontSize();
13327
13508
  }
13328
13509
  async resetFontSize(context2) {
13329
- const size = this.getDefault();
13510
+ const size = this.getDefault(context2);
13330
13511
  if (context2.terminal?.options) {
13331
13512
  context2.terminal.options.fontSize = size;
13332
13513
  }
@@ -21960,73 +22141,6 @@ function createServerModule() {
21960
22141
  services: []
21961
22142
  };
21962
22143
  }
21963
- var CLI_EVENT_HISTORY_SIZE = 200;
21964
- function matchesEventPattern(type, pattern) {
21965
- if (pattern === "*") return true;
21966
- if (pattern.endsWith(":*")) {
21967
- return type.startsWith(pattern.slice(0, -1));
21968
- }
21969
- return type === pattern;
21970
- }
21971
- var CliEventBus = class {
21972
- constructor(onHandlerError = (error) => console.error("[CLI] Event handler failed:", error), historySize = CLI_EVENT_HISTORY_SIZE) {
21973
- this.onHandlerError = onHandlerError;
21974
- this.historySize = historySize;
21975
- this.subject = new rxjs.Subject();
21976
- this.recent = [];
21977
- this.disposed = false;
21978
- }
21979
- emit(type, data, options) {
21980
- if (this.disposed) return;
21981
- const event = {
21982
- type,
21983
- data,
21984
- source: options?.source,
21985
- timestamp: Date.now()
21986
- };
21987
- this.recent.push(event);
21988
- if (this.recent.length > this.historySize) {
21989
- this.recent.shift();
21990
- }
21991
- this.subject.next(event);
21992
- }
21993
- on(pattern, handler) {
21994
- const subscription = this.events$(pattern).subscribe((event) => {
21995
- try {
21996
- handler(event);
21997
- } catch (error) {
21998
- this.onHandlerError(error, event);
21999
- }
22000
- });
22001
- return () => subscription.unsubscribe();
22002
- }
22003
- once(pattern, handler) {
22004
- let done = false;
22005
- const unsubscribe = this.on(pattern, (event) => {
22006
- if (done) return;
22007
- done = true;
22008
- unsubscribe();
22009
- handler(event);
22010
- });
22011
- return unsubscribe;
22012
- }
22013
- events$(pattern = "*") {
22014
- return this.subject.asObservable().pipe(operators.filter((event) => matchesEventPattern(event.type, pattern)));
22015
- }
22016
- history(pattern = "*", limit) {
22017
- const matching = this.recent.filter(
22018
- (event) => matchesEventPattern(event.type, pattern)
22019
- );
22020
- return limit !== void 0 ? matching.slice(-limit) : matching;
22021
- }
22022
- /** Stop delivering events and complete all observables. */
22023
- dispose() {
22024
- if (this.disposed) return;
22025
- this.disposed = true;
22026
- this.subject.complete();
22027
- this.recent.length = 0;
22028
- }
22029
- };
22030
22144
 
22031
22145
  // src/lib/editor/syntax/registry.ts
22032
22146
  var SyntaxHighlighterRegistry = class {
@@ -23128,12 +23242,13 @@ var CliEngine = class {
23128
23242
  });
23129
23243
  }
23130
23244
  /**
23131
- * Restyle the terminal: its colors and the background around it.
23132
- * The `theme` command can still change it afterwards.
23245
+ * The host's theme for this terminal: its colors and the background around it.
23246
+ * A theme the user chose with the `theme` command stays until `theme reset`,
23247
+ * which returns to this one.
23133
23248
  */
23134
23249
  setTheme(theme) {
23135
- if (this.terminal) this.terminal.options.theme = theme;
23136
- this.container.style.background = theme.background ?? "";
23250
+ if (this.terminal) setHostTerminalTheme(this.terminal, theme);
23251
+ else this.container.style.background = theme.background ?? "";
23137
23252
  }
23138
23253
  /**
23139
23254
  * Get the underlying xterm.js Terminal instance.
@@ -23238,6 +23353,8 @@ var CliEngine = class {
23238
23353
  window.addEventListener("resize", this.resizeListener);
23239
23354
  this.resizeObserver = new ResizeObserver(() => this.safeFit());
23240
23355
  this.resizeObserver.observe(this.container);
23356
+ const screen2 = this.terminal?.element?.querySelector(".xterm-screen");
23357
+ if (screen2) this.resizeObserver.observe(screen2);
23241
23358
  }
23242
23359
  /**
23243
23360
  * Call fitAddon.fit() only when the container has non-zero dimensions.
@@ -23984,6 +24101,9 @@ function sameJson(a, b) {
23984
24101
  }
23985
24102
  var PANE_RESIZING_BODY_CLASS = "cli-pane-resizing";
23986
24103
  var DARK_SCHEME_QUERY = "(prefers-color-scheme: dark)";
24104
+ function asEventTarget(node) {
24105
+ return node && typeof node.addEventListener === "function" ? node : null;
24106
+ }
23987
24107
  function followsSystem(input) {
23988
24108
  return input === "auto" || typeof input === "object" && input !== null && input.base === "auto";
23989
24109
  }
@@ -24058,6 +24178,13 @@ var CliPanelController = class {
24058
24178
  this.cancelRename();
24059
24179
  };
24060
24180
  this.onSystemSchemeChange = () => this.applyTheme();
24181
+ /** A terminal in the panel changed theme: follow the first one. */
24182
+ this.onTerminalTheme = (event) => {
24183
+ if (!this.followsTerminalTheme) return;
24184
+ const first = this.rootElement?.querySelector(".terminal-container");
24185
+ if (event.target !== first) return;
24186
+ this.syncThemeFromEngine(event.detail);
24187
+ };
24061
24188
  this.onVisualViewportChange = () => {
24062
24189
  const fields = visualViewportFields();
24063
24190
  const s = this.snapshot;
@@ -24130,7 +24257,7 @@ var CliPanelController = class {
24130
24257
  return;
24131
24258
  }
24132
24259
  this.commit(fields);
24133
- if (fields.syncTheme) this.startThemeSync();
24260
+ if (this.followsTerminalTheme) this.startThemeSync();
24134
24261
  else this.stopThemeSync();
24135
24262
  }
24136
24263
  /**
@@ -24141,6 +24268,15 @@ var CliPanelController = class {
24141
24268
  this.themeInput = theme;
24142
24269
  this.watchSystemScheme();
24143
24270
  this.applyTheme();
24271
+ if (this.followsTerminalTheme) this.startThemeSync();
24272
+ else this.stopThemeSync();
24273
+ }
24274
+ /**
24275
+ * Whether the panel's colors follow its first terminal: with `syncTheme`, or
24276
+ * with a host theme so a theme the user applies restyles the panel too.
24277
+ */
24278
+ get followsTerminalTheme() {
24279
+ return this.snapshot.syncTheme || this.themeInput !== void 0;
24144
24280
  }
24145
24281
  /** Replace the change callbacks (wrappers call this when their props change). */
24146
24282
  setCallbacks(callbacks) {
@@ -24156,8 +24292,11 @@ var CliPanelController = class {
24156
24292
  }
24157
24293
  /** Element containing the terminals, used for theme sync. */
24158
24294
  setRootElement(element) {
24295
+ if (element === this.rootElement) return;
24296
+ asEventTarget(this.rootElement)?.removeEventListener(CLI_TERMINAL_THEME_EVENT, this.onTerminalTheme);
24159
24297
  this.rootElement = element;
24160
- if (element && this.snapshot.syncTheme) this.startThemeSync();
24298
+ asEventTarget(element)?.addEventListener(CLI_TERMINAL_THEME_EVENT, this.onTerminalTheme);
24299
+ if (element && this.followsTerminalTheme) this.startThemeSync();
24161
24300
  }
24162
24301
  /**
24163
24302
  * Stop timers, observers and document listeners. Engines are not
@@ -24385,10 +24524,11 @@ var CliPanelController = class {
24385
24524
  /** Called by the view once a pane's terminal engine is running. */
24386
24525
  registerEngine(tabId, paneId, engine) {
24387
24526
  if (!this.findTab(tabId)) return;
24527
+ const known = this.engines.get(paneId) === engine;
24388
24528
  this.engines.set(paneId, engine);
24389
24529
  this.tracker.registerEngine(tabId, paneId, engine);
24390
- if (this.snapshot.terminalTheme) engine.setTheme(this.snapshot.terminalTheme);
24391
- if (this.snapshot.syncTheme) this.startThemeSync();
24530
+ if (this.snapshot.terminalTheme && !known) engine.setTheme(this.snapshot.terminalTheme);
24531
+ if (this.followsTerminalTheme) this.startThemeSync();
24392
24532
  }
24393
24533
  getEngine(paneId) {
24394
24534
  return this.engines.get(paneId ?? this.snapshot.activePaneId);
@@ -24533,11 +24673,12 @@ var CliPanelController = class {
24533
24673
  this.documentListenersAttached = false;
24534
24674
  }
24535
24675
  /* ─── Theme sync ────────────────────────────────────────── */
24536
- /** Read the first terminal's theme and derive the panel colours from it. */
24537
- syncThemeFromEngine() {
24538
- const engine = this.engines.values().next().value;
24539
- if (!engine) return;
24540
- const theme = engine.getTerminal().options.theme;
24676
+ /** Read the first terminal's theme (or use `theme`) and derive the panel colours from it. */
24677
+ syncThemeFromEngine(theme) {
24678
+ if (!theme) {
24679
+ const engine = this.engines.values().next().value;
24680
+ theme = engine?.getTerminal().options.theme;
24681
+ }
24541
24682
  if (!theme) return;
24542
24683
  const styles = sameTerminalTheme(theme, this.resolvedTheme.terminal) ? this.resolvedTheme.styles : cliCore.derivePanelThemeStyles(theme);
24543
24684
  if (sameRecord(styles, this.snapshot.themeStyles)) return;
@@ -24564,7 +24705,7 @@ var CliPanelController = class {
24564
24705
  const themeStyles = this.themeInput === void 0 ? {} : resolved.styles;
24565
24706
  this.commit({ themeStyles, scheme: resolved.scheme, terminalTheme });
24566
24707
  if (terminalChanged) this.engines.forEach((engine) => engine.setTheme(terminalTheme));
24567
- if (this.snapshot.syncTheme) this.syncThemeFromEngine();
24708
+ if (this.followsTerminalTheme) this.syncThemeFromEngine();
24568
24709
  }
24569
24710
  /** Follow the system's light/dark setting while the theme is 'auto'. */
24570
24711
  watchSystemScheme() {
@@ -24663,7 +24804,7 @@ var CliPanelController = class {
24663
24804
  if (this.themeObserver) {
24664
24805
  this.stopThemeSync();
24665
24806
  setTimeout(() => {
24666
- if (this.snapshot.syncTheme && !this.themeObserver) this.startThemeSync();
24807
+ if (this.followsTerminalTheme && !this.themeObserver) this.startThemeSync();
24667
24808
  }, 0);
24668
24809
  }
24669
24810
  try {