omnius 1.0.865 → 1.0.867

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 (37) hide show
  1. package/assets/fonts/DepartureMono-LICENSE.txt +93 -0
  2. package/assets/licenses/Prism-1.29.0-LICENSE.txt +21 -0
  3. package/dist/api/py-embed.js +19 -4
  4. package/dist/index.js +2 -2
  5. package/dist/library.js +1074 -972
  6. package/dist/tui/native/worker.js +392 -172
  7. package/dist/update-worker.js +19 -4
  8. package/docs/DISCOVERY.json +3896 -2630
  9. package/docs/DISCOVERY.md +28 -2
  10. package/docs/design/dashboard-TODO.md +86 -0
  11. package/docs/design/dashboard-reconstruction-plan.md +58 -0
  12. package/docs/design/dashboard-styleguide.md +86 -0
  13. package/docs/design/media-unification-TODO.md +58 -0
  14. package/docs/work-orders/2026-10-16-browser-instrument-dashboard.md +17 -0
  15. package/docs/work-orders/2026-10-16-interface-delivery.md +6 -0
  16. package/docs/work-orders/evidence/browser-controls-deployment-20261016.json +28 -0
  17. package/docs/work-orders/evidence/browser-controls-package-verification-20261016.json +68 -0
  18. package/docs/work-orders/evidence/dashboard-browser-reconstruction-20261016.json +75 -0
  19. package/docs/work-orders/evidence/dashboard-media-management-20261016.json +145 -0
  20. package/docs/work-orders/evidence/instrument-package-install-20261016-updated.json +45 -6
  21. package/docs/work-orders/evidence/instrument-sessions-narrow-60x20.png +0 -0
  22. package/docs/work-orders/evidence/instrument-sessions-restored-120x36.png +0 -0
  23. package/docs/work-orders/evidence/native-session-lifecycle-verification-20261016.json +79 -0
  24. package/docs/work-orders/evidence/native-session-regression-verification-20261016.json +65 -0
  25. package/docs/work-orders/evidence/native-session-widget-verification-20261016.json +60 -0
  26. package/docs/work-orders/evidence/native-sessions-installed-pty-20261016.json +2608 -0
  27. package/docs/work-orders/evidence/native-sessions-production-pty-20261016.json +3348 -0
  28. package/docs/work-orders/evidence/native-sessions-production-vte-20261016.json +2032 -0
  29. package/docs/work-orders/evidence/native-straight-boundaries-verification-20261016.json +36 -0
  30. package/docs/work-orders/evidence/session-tabs-installation-20261016.json +37 -0
  31. package/docs/work-orders/evidence/session-tabs-package-verification-20261016.json +50 -0
  32. package/docs/work-orders/evidence/web-sidebar-send-dark-desktop-20261016.png +0 -0
  33. package/docs/work-orders/evidence/web-sidebar-send-light-phone-20261016.png +0 -0
  34. package/docs/work-orders/evidence/web-sidebar-send-repair-20261016.json +163 -0
  35. package/docs/work-orders/instrument-tui.md +44 -4
  36. package/npm-shrinkwrap.json +15 -15
  37. package/package.json +1 -1
@@ -2,19 +2,18 @@
2
2
  import { createWriteStream } from "node:fs";
3
3
  import { createInterface } from "node:readline";
4
4
  import { PassThrough } from "node:stream";
5
- import { CliRenderEvents as CliRenderEvents6, NativeSession, RGBA as RGBA3, createCliRenderer } from "@opentui/core";
5
+ import { CliRenderEvents as CliRenderEvents7, NativeSession, RGBA as RGBA3, createCliRenderer } from "@opentui/core";
6
6
 
7
7
  // packages/cli/src/tui/native/components/frontend.ts
8
8
  import {
9
- BoxRenderable as BoxRenderable6,
10
- CliRenderEvents as CliRenderEvents4,
9
+ BoxRenderable as BoxRenderable7,
10
+ CliRenderEvents as CliRenderEvents5,
11
11
  EmbeddedTerminalRenderable as EmbeddedTerminalRenderable2,
12
- ScrollBoxRenderable as ScrollBoxRenderable4,
12
+ ScrollBoxRenderable as ScrollBoxRenderable5,
13
13
  SyntaxStyle as SyntaxStyle2,
14
- TabSelectRenderable,
15
14
  TextareaRenderable as TextareaRenderable2,
16
- TextRenderable as TextRenderable5,
17
- TextAttributes as TextAttributes7,
15
+ TextRenderable as TextRenderable6,
16
+ TextAttributes as TextAttributes8,
18
17
  createClipboard,
19
18
  createHostClipboard,
20
19
  createRendererClipboardAdapter
@@ -799,49 +798,51 @@ function drawDotRail(buffer, x, y, length, horizontal, progress, colors) {
799
798
  function styleNativeScrollbars(scroll, colors) {
800
799
  for (const bar of [scroll.horizontalScrollBar, scroll.verticalScrollBar]) {
801
800
  bar.showArrows = false;
802
- const slider = bar.slider;
803
- slider.foregroundColor = colors.foreground;
804
- slider.backgroundColor = colors.background;
805
- slider.renderAfter = (buffer) => {
806
- const horizontal = slider.orientation === "horizontal";
807
- drawDotRail(
808
- buffer,
809
- slider.x,
810
- slider.y,
811
- horizontal ? slider.width : slider.height,
812
- horizontal,
813
- slider.max > slider.min ? (slider.value - slider.min) / (slider.max - slider.min) : 0,
814
- colors
815
- );
816
- };
817
- let dragging = false;
818
- const seek = (event) => {
819
- const horizontal = slider.orientation === "horizontal";
820
- const length = horizontal ? slider.width : slider.height;
821
- const offset = horizontal ? event.x - slider.x : event.y - slider.y;
822
- const progress = length > 1 ? Math.max(0, Math.min(1, offset / (length - 1))) : 0;
823
- slider.value = slider.min + progress * (slider.max - slider.min);
824
- event.preventDefault();
825
- event.stopPropagation();
826
- };
827
- slider.onMouseDown = (event) => {
828
- if (event.button === 0) {
829
- dragging = true;
830
- seek(event);
831
- }
832
- };
833
- slider.onMouseDrag = (event) => {
834
- if (dragging) seek(event);
835
- };
836
- slider.onMouseUp = (event) => {
837
- if (dragging) seek(event);
838
- dragging = false;
839
- };
840
- slider.onMouseDragEnd = () => {
841
- dragging = false;
842
- };
801
+ styleNativeSlider(bar.slider, colors);
843
802
  }
844
803
  }
804
+ function styleNativeSlider(slider, colors) {
805
+ slider.foregroundColor = colors.foreground;
806
+ slider.backgroundColor = colors.background;
807
+ slider.renderAfter = (buffer) => {
808
+ const horizontal = slider.orientation === "horizontal";
809
+ drawDotRail(
810
+ buffer,
811
+ slider.x,
812
+ slider.y,
813
+ horizontal ? slider.width : slider.height,
814
+ horizontal,
815
+ slider.max > slider.min ? (slider.value - slider.min) / (slider.max - slider.min) : 0,
816
+ colors
817
+ );
818
+ };
819
+ let dragging = false;
820
+ const seek = (event) => {
821
+ const horizontal = slider.orientation === "horizontal";
822
+ const length = horizontal ? slider.width : slider.height;
823
+ const offset = horizontal ? event.x - slider.x : event.y - slider.y;
824
+ const progress = length > 1 ? Math.max(0, Math.min(1, offset / (length - 1))) : 0;
825
+ slider.value = slider.min + progress * (slider.max - slider.min);
826
+ event.preventDefault();
827
+ event.stopPropagation();
828
+ };
829
+ slider.onMouseDown = (event) => {
830
+ if (event.button === 0) {
831
+ dragging = true;
832
+ seek(event);
833
+ }
834
+ };
835
+ slider.onMouseDrag = (event) => {
836
+ if (dragging) seek(event);
837
+ };
838
+ slider.onMouseUp = (event) => {
839
+ if (dragging) seek(event);
840
+ dragging = false;
841
+ };
842
+ slider.onMouseDragEnd = () => {
843
+ dragging = false;
844
+ };
845
+ }
845
846
 
846
847
  // packages/cli/src/tui/native/components/select.ts
847
848
  var NativeSelectRenderable = class extends SelectRenderable {
@@ -1781,12 +1782,11 @@ function terminalCellWidth(value) {
1781
1782
 
1782
1783
  // packages/cli/src/tui/native/components/instrument-format.ts
1783
1784
  var graphemes2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1784
- function formatSignalDivider(width) {
1785
+ function formatSignalDivider(width, geometry = { direction: "top" }) {
1785
1786
  const cells = Math.max(0, Math.floor(width));
1786
- if (cells < 32) return "─".repeat(cells);
1787
- const plateau = Math.min(12, Math.max(6, Math.floor(cells / 8)));
1788
- const left = Math.floor((cells - plateau - 2) / 2);
1789
- return "▁".repeat(left) + "╱" + "▔".repeat(plateau) + "╲" + "▁".repeat(cells - left - plateau - 2);
1787
+ const column = Math.floor(geometry.sidebarColumn ?? -1);
1788
+ if (!Number.isFinite(column) || column < 0 || column >= cells) return "─".repeat(cells);
1789
+ return "─".repeat(column) + (geometry.direction === "top" ? "┬" : "┴") + "─".repeat(cells - column - 1);
1790
1790
  }
1791
1791
  function fitInstrumentText(value, cells, marker = "~") {
1792
1792
  const text = stripTerminalSequences(normalizeTerminalLine(value));
@@ -4206,6 +4206,180 @@ var NativeSelectionMenu = class {
4206
4206
  }
4207
4207
  };
4208
4208
 
4209
+ // packages/cli/src/tui/native/components/tab-strip.ts
4210
+ import { BoxRenderable as BoxRenderable6, CliRenderEvents as CliRenderEvents4, ScrollBoxRenderable as ScrollBoxRenderable4, SliderRenderable, TextAttributes as TextAttributes7, TextRenderable as TextRenderable5 } from "@opentui/core";
4211
+ var NativeTabStrip = class extends BoxRenderable6 {
4212
+ constructor(renderer2, options) {
4213
+ super(renderer2, {
4214
+ id: options.id,
4215
+ height: 1,
4216
+ minHeight: 1,
4217
+ maxHeight: 1,
4218
+ minWidth: 0,
4219
+ flexGrow: 1,
4220
+ flexShrink: 1,
4221
+ flexDirection: "row",
4222
+ shouldFill: false
4223
+ });
4224
+ this.renderer = renderer2;
4225
+ this.options = options;
4226
+ this.colors = options.colors;
4227
+ this.scroll = new ScrollBoxRenderable4(renderer2, {
4228
+ id: `${options.id}:scroll`,
4229
+ height: 1,
4230
+ minHeight: 1,
4231
+ maxHeight: 1,
4232
+ minWidth: 0,
4233
+ flexGrow: 1,
4234
+ flexShrink: 1,
4235
+ scrollX: true,
4236
+ scrollY: false,
4237
+ viewportCulling: true,
4238
+ viewportOptions: { height: 1, minHeight: 1, maxHeight: 1 },
4239
+ contentOptions: { flexDirection: "row", gap: 1 },
4240
+ horizontalScrollbarOptions: { visible: false },
4241
+ verticalScrollbarOptions: { visible: false }
4242
+ });
4243
+ this.scroll.focusable = false;
4244
+ this.scroll.horizontalScrollBar.visible = this.scroll.verticalScrollBar.visible = false;
4245
+ styleNativeScrollbars(this.scroll, this.colors);
4246
+ this.add(this.scroll);
4247
+ this.rail = new SliderRenderable(renderer2, {
4248
+ id: `${options.id}:rail`,
4249
+ orientation: "horizontal",
4250
+ width: 5,
4251
+ minWidth: 5,
4252
+ height: 1,
4253
+ flexShrink: 0,
4254
+ visible: false,
4255
+ min: 0,
4256
+ max: 0,
4257
+ value: 0,
4258
+ onChange: (value) => {
4259
+ if (!this.syncing) this.scroll.scrollLeft = value;
4260
+ }
4261
+ });
4262
+ this.rail.focusable = false;
4263
+ this.add(this.rail);
4264
+ styleNativeSlider(this.rail, this.colors);
4265
+ renderer2.on(CliRenderEvents4.FRAME, this.syncScroll);
4266
+ }
4267
+ renderer;
4268
+ options;
4269
+ scroll;
4270
+ rail;
4271
+ buttons = /* @__PURE__ */ new Map();
4272
+ activeId;
4273
+ signature = "";
4274
+ syncing = false;
4275
+ reveal;
4276
+ colors;
4277
+ naturalWidth = 0;
4278
+ update(items, activeId, busy = false) {
4279
+ const signature = JSON.stringify([items, activeId, busy]);
4280
+ if (signature === this.signature) return;
4281
+ this.signature = signature;
4282
+ const ids = new Set(items.map((item) => item.id));
4283
+ for (const [id, entry] of this.buttons) if (!ids.has(id)) {
4284
+ entry.button.destroy();
4285
+ this.buttons.delete(id);
4286
+ }
4287
+ for (const [index, action] of items.entries()) {
4288
+ let entry = this.buttons.get(action.id);
4289
+ if (!entry) {
4290
+ const button = new TextRenderable5(this.renderer, {
4291
+ id: `${this.options.prefix}:${action.id}`,
4292
+ content: "",
4293
+ height: 1,
4294
+ flexShrink: 0,
4295
+ wrapMode: "none",
4296
+ selectable: false,
4297
+ fg: this.colors.foreground,
4298
+ bg: this.colors.background,
4299
+ onMouseDown: (event) => {
4300
+ if (event.button === 0) {
4301
+ button.focus();
4302
+ if (!entry.blocked) this.options.activate(action.id);
4303
+ }
4304
+ event.preventDefault();
4305
+ event.stopPropagation();
4306
+ },
4307
+ onKeyDown: (key) => this.handleKey(entry, key)
4308
+ });
4309
+ entry = { action, active: false, blocked: false, button };
4310
+ this.buttons.set(action.id, entry);
4311
+ button.on("focused", () => {
4312
+ this.style(entry);
4313
+ this.scroll.scrollChildIntoView(button.id);
4314
+ this.reveal = button.id;
4315
+ });
4316
+ button.on("blurred", () => this.style(entry));
4317
+ }
4318
+ entry.action = action;
4319
+ entry.active = action.id === activeId;
4320
+ entry.blocked = busy || Boolean(action.disabled);
4321
+ entry.button.focusable = entry.active || !entry.blocked;
4322
+ const label = plainText(action.label);
4323
+ const content = ` ${label} `;
4324
+ if (entry.button.plainText !== content) entry.button.content = content;
4325
+ this.style(entry);
4326
+ if (this.scroll.getChildren()[index] !== entry.button) this.scroll.add(entry.button, index);
4327
+ }
4328
+ this.naturalWidth = Array.from(this.buttons.values()).reduce((width, entry) => width + terminalCellWidth(entry.button.plainText), 0) + Math.max(0, items.length - 1);
4329
+ const focused = this.renderer.currentFocusedRenderable;
4330
+ if (focused && !focused.focusable && Array.from(this.buttons.values()).some((entry) => entry.button === focused)) this.buttons.get(activeId)?.button.focus();
4331
+ if (activeId !== this.activeId) this.reveal = `${this.options.prefix}:${activeId}`;
4332
+ this.activeId = activeId;
4333
+ this.renderer.requestRender();
4334
+ }
4335
+ style(entry) {
4336
+ entry.button.attributes = (entry.active ? TextAttributes7.BOLD | TextAttributes7.UNDERLINE : entry.blocked ? TextAttributes7.DIM : 0) | (entry.button.focused ? TextAttributes7.INVERSE : 0);
4337
+ }
4338
+ handleKey(entry, key) {
4339
+ if (key.eventType === "release") return;
4340
+ if (key.name === "return" || key.sequence === " ") {
4341
+ if (!entry.blocked) this.options.activate(entry.action.id);
4342
+ } else if (["left", "right", "home", "end"].includes(key.name)) {
4343
+ const targets = this.scroll.getChildren().filter((button) => button.focusable);
4344
+ const index = targets.indexOf(entry.button);
4345
+ const next = key.name === "home" ? 0 : key.name === "end" ? targets.length - 1 : (index + (key.name === "left" ? -1 : 1) + targets.length) % targets.length;
4346
+ targets[next]?.focus();
4347
+ } else return;
4348
+ key.preventDefault();
4349
+ key.stopPropagation();
4350
+ }
4351
+ syncScroll = () => {
4352
+ if (!this.visible || this.width < 1) return;
4353
+ const overflow = this.naturalWidth > this.width;
4354
+ if (this.rail.visible !== overflow) {
4355
+ this.rail.visible = overflow;
4356
+ this.renderer.requestRender();
4357
+ }
4358
+ if (this.reveal) {
4359
+ this.scroll.scrollChildIntoView(this.reveal);
4360
+ this.reveal = void 0;
4361
+ }
4362
+ this.syncing = true;
4363
+ this.rail.max = Math.max(0, this.scroll.scrollWidth - this.scroll.viewport.width);
4364
+ this.rail.viewPortSize = this.scroll.viewport.width;
4365
+ this.rail.value = this.scroll.scrollLeft;
4366
+ this.syncing = false;
4367
+ };
4368
+ applyTheme(colors) {
4369
+ this.colors = colors;
4370
+ for (const { button } of this.buttons.values()) {
4371
+ button.fg = colors.foreground;
4372
+ button.bg = colors.background;
4373
+ }
4374
+ styleNativeScrollbars(this.scroll, colors);
4375
+ styleNativeSlider(this.rail, colors);
4376
+ }
4377
+ destroySelf() {
4378
+ this.renderer.off(CliRenderEvents4.FRAME, this.syncScroll);
4379
+ super.destroySelf();
4380
+ }
4381
+ };
4382
+
4209
4383
  // packages/cli/src/tui/native/components/frontend.ts
4210
4384
  var initialTheme = { mode: "system", bg: -1, accent: -1, textPrimary: -1, textDim: -1, boxColor: -1 };
4211
4385
  var NativeFrontend = class {
@@ -4219,7 +4393,7 @@ var NativeFrontend = class {
4219
4393
  this.selectionMenu = new NativeSelectionMenu(renderer2, colors, (text) => {
4220
4394
  void this.copy(text);
4221
4395
  }, () => this.restoreFocus());
4222
- this.root = new BoxRenderable6(renderer2, {
4396
+ this.root = new BoxRenderable7(renderer2, {
4223
4397
  id: "omnius-native",
4224
4398
  width: "100%",
4225
4399
  height: "100%",
@@ -4227,7 +4401,7 @@ var NativeFrontend = class {
4227
4401
  backgroundColor: colors.background
4228
4402
  });
4229
4403
  renderer2.root.add(this.root);
4230
- this.header = new BoxRenderable6(renderer2, {
4404
+ this.header = new BoxRenderable7(renderer2, {
4231
4405
  id: "omnius-header",
4232
4406
  width: "100%",
4233
4407
  minWidth: 0,
@@ -4240,17 +4414,20 @@ var NativeFrontend = class {
4240
4414
  this.root.add(this.header);
4241
4415
  this.header.renderAfter = (buffer) => {
4242
4416
  if (this.header.height > 0) buffer.drawText(
4243
- formatSignalDivider(this.header.width),
4417
+ formatSignalDivider(this.header.width, {
4418
+ direction: "top",
4419
+ sidebarColumn: this.instrumentDivider.visible ? this.instrumentDivider.x - this.header.x : void 0
4420
+ }),
4244
4421
  this.header.x,
4245
4422
  this.header.y + this.header.height - 1,
4246
4423
  this.context.colors.foreground,
4247
4424
  this.context.colors.background,
4248
- TextAttributes7.DIM
4425
+ TextAttributes8.DIM
4249
4426
  );
4250
4427
  };
4251
- const headerRow = new BoxRenderable6(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
4428
+ const headerRow = new BoxRenderable7(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
4252
4429
  this.header.add(headerRow);
4253
- this.identity = new TextRenderable5(renderer2, {
4430
+ this.identity = new TextRenderable6(renderer2, {
4254
4431
  id: "omnius-identity",
4255
4432
  content: "Omnius",
4256
4433
  fg: colors.foreground,
@@ -4260,7 +4437,7 @@ var NativeFrontend = class {
4260
4437
  truncate: true,
4261
4438
  minWidth: 18,
4262
4439
  selectable: false,
4263
- attributes: TextAttributes7.BOLD,
4440
+ attributes: TextAttributes8.BOLD,
4264
4441
  onMouseDown: (event) => {
4265
4442
  if (event.button === 0) {
4266
4443
  this.toggleHeaderPanel();
@@ -4275,7 +4452,7 @@ var NativeFrontend = class {
4275
4452
  }
4276
4453
  });
4277
4454
  headerRow.add(this.identity);
4278
- this.headerActions = new ScrollBoxRenderable4(renderer2, {
4455
+ this.headerActions = new ScrollBoxRenderable5(renderer2, {
4279
4456
  id: "omnius-header-actions",
4280
4457
  flexGrow: 0,
4281
4458
  flexShrink: 1,
@@ -4288,16 +4465,16 @@ var NativeFrontend = class {
4288
4465
  verticalScrollbarOptions: { visible: false }
4289
4466
  });
4290
4467
  headerRow.add(this.headerActions);
4291
- this.headerPanel = new TextRenderable5(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
4468
+ this.headerPanel = new TextRenderable6(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
4292
4469
  this.header.add(this.headerPanel);
4293
- this.detailButton = new TextRenderable5(renderer2, {
4470
+ this.detailButton = new TextRenderable6(renderer2, {
4294
4471
  id: "omnius-details-toggle",
4295
4472
  content: "+ DETAILS",
4296
4473
  height: 1,
4297
4474
  width: 9,
4298
4475
  flexShrink: 0,
4299
4476
  fg: colors.foreground,
4300
- attributes: TextAttributes7.DIM,
4477
+ attributes: TextAttributes8.DIM,
4301
4478
  selectable: false,
4302
4479
  onMouseDown: (event) => {
4303
4480
  if (event.button === 0) {
@@ -4317,60 +4494,67 @@ var NativeFrontend = class {
4317
4494
  for (const control of [this.identity, this.detailButton]) {
4318
4495
  const attributes = control.attributes;
4319
4496
  control.on("focused", () => {
4320
- control.attributes = attributes | TextAttributes7.INVERSE;
4497
+ control.attributes = attributes | TextAttributes8.INVERSE;
4321
4498
  });
4322
4499
  control.on("blurred", () => {
4323
4500
  control.attributes = attributes;
4324
4501
  });
4325
4502
  }
4326
- this.routeWarning = new TextRenderable5(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes7.BOLD | TextAttributes7.UNDERLINE, visible: false, flexShrink: 0 });
4327
- this.root.add(this.routeWarning);
4328
- this.tabs = new TabSelectRenderable(renderer2, {
4329
- id: "omnius-agent-tabs",
4503
+ this.sessionRow = new BoxRenderable7(renderer2, {
4504
+ id: "omnius-sessions-row",
4330
4505
  width: "100%",
4331
- height: 2,
4332
- showDescription: false,
4333
- showUnderline: true,
4334
- showScrollArrows: false,
4335
- tabWidth: 18,
4336
- wrapSelection: true,
4337
- visible: false,
4338
- backgroundColor: colors.background,
4339
- focusedBackgroundColor: colors.background,
4340
- textColor: colors.dim,
4341
- selectedTextColor: colors.foreground,
4342
- selectedBackgroundColor: colors.selection,
4506
+ height: 1,
4507
+ minWidth: 0,
4508
+ flexShrink: 0,
4509
+ flexDirection: "row",
4510
+ gap: 1,
4511
+ visible: false
4512
+ });
4513
+ this.header.add(this.sessionRow);
4514
+ this.sessions = new NativeTabStrip(renderer2, {
4515
+ id: "omnius-session-tabs",
4516
+ prefix: "session",
4517
+ colors,
4518
+ activate: (id) => this.emitAction("session", id)
4519
+ });
4520
+ this.sessionRow.add(this.sessions);
4521
+ this.newSession = new TextRenderable6(renderer2, {
4522
+ id: "omnius-session-new",
4523
+ content: "+ NEW",
4524
+ width: 5,
4525
+ height: 1,
4526
+ flexShrink: 0,
4527
+ selectable: false,
4528
+ fg: colors.foreground,
4529
+ bg: colors.background,
4343
4530
  onMouseDown: (event) => {
4344
- if (event.button !== 0) return;
4345
- const count = Math.max(1, Math.floor(this.tabs.width / this.tabs.getTabWidth()));
4346
- const start = Math.max(0, Math.min(this.tabs.getSelectedIndex() - Math.floor(count / 2), this.tabs.options.length - count));
4347
- this.tabs.setSelectedIndex(start + Math.floor((event.x - this.tabs.x) / this.tabs.getTabWidth()));
4531
+ if (event.button === 0 && !this.state?.sessions?.busy) this.emitAction("session-new");
4532
+ event.preventDefault();
4348
4533
  event.stopPropagation();
4534
+ },
4535
+ onKeyDown: (key) => {
4536
+ if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
4537
+ if (!this.state?.sessions?.busy) this.emitAction("session-new");
4538
+ key.preventDefault();
4539
+ key.stopPropagation();
4540
+ }
4349
4541
  }
4350
4542
  });
4351
- this.tabs.on("selectionChanged", () => {
4352
- if (this.changing) return;
4353
- const selected = this.tabs.getSelectedOption();
4354
- if (selected) this.emitAction("view", String(selected.value));
4355
- this.editor.focus();
4356
- });
4357
- this.tabs.renderAfter = (buffer) => {
4358
- if (this.tabs.height < 2) return;
4359
- const colors2 = this.context.colors;
4360
- const tabWidth = Math.min(this.tabs.getTabWidth(), this.tabs.width);
4361
- const count = Math.max(1, Math.floor(this.tabs.width / Math.max(1, tabWidth)));
4362
- const selected = this.tabs.getSelectedIndex();
4363
- const start = Math.max(0, Math.min(selected - Math.floor(count / 2), this.tabs.options.length - count));
4364
- buffer.fillRect(0, 1, this.tabs.width, 1, colors2.background);
4365
- buffer.drawText("─".repeat(Math.max(0, tabWidth)), (selected - start) * tabWidth, 1, colors2.foreground, colors2.background, TextAttributes7.DIM);
4543
+ this.newSession.focusable = true;
4544
+ const styleNew = () => {
4545
+ this.newSession.attributes = (this.state?.sessions?.busy ? TextAttributes8.DIM : 0) | (this.newSession.focused ? TextAttributes8.INVERSE : 0);
4366
4546
  };
4367
- this.root.add(this.tabs);
4368
- this.taskPane = new BoxRenderable6(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
4547
+ this.newSession.on("focused", styleNew);
4548
+ this.newSession.on("blurred", styleNew);
4549
+ this.sessionRow.add(this.newSession);
4550
+ this.routeWarning = new TextRenderable6(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes8.BOLD | TextAttributes8.UNDERLINE, visible: false, flexShrink: 0 });
4551
+ this.root.add(this.routeWarning);
4552
+ this.taskPane = new BoxRenderable7(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
4369
4553
  this.tasks = new NativeBlockList(this.context, this.taskPane);
4370
4554
  this.root.add(this.taskPane);
4371
- this.body = new BoxRenderable6(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
4555
+ this.body = new BoxRenderable7(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
4372
4556
  this.root.add(this.body);
4373
- this.scroll = new ScrollBoxRenderable4(renderer2, {
4557
+ this.scroll = new ScrollBoxRenderable5(renderer2, {
4374
4558
  id: "omnius-conversation",
4375
4559
  flexGrow: 1,
4376
4560
  flexShrink: 1,
@@ -4443,7 +4627,7 @@ var NativeFrontend = class {
4443
4627
  this.root.onMouseUp = (event) => this.instrumentDivider.handleRelease(event);
4444
4628
  this.instruments = new InstrumentRail(renderer2, { colors, onInspectAgent: (id) => this.emitAction("view", id), onAction: (command2) => this.emitAction(command2) });
4445
4629
  this.body.add(this.instruments);
4446
- this.embeddedPane = new BoxRenderable6(renderer2, {
4630
+ this.embeddedPane = new BoxRenderable7(renderer2, {
4447
4631
  id: "omnius-editor-pane",
4448
4632
  width: "50%",
4449
4633
  flexShrink: 0,
@@ -4454,7 +4638,7 @@ var NativeFrontend = class {
4454
4638
  visible: false
4455
4639
  });
4456
4640
  this.body.add(this.embeddedPane);
4457
- this.liveButton = new TextRenderable5(renderer2, {
4641
+ this.liveButton = new TextRenderable6(renderer2, {
4458
4642
  id: "omnius-live-button",
4459
4643
  content: "↓ Live",
4460
4644
  width: "100%",
@@ -4477,7 +4661,7 @@ var NativeFrontend = class {
4477
4661
  });
4478
4662
  this.liveButton.focusable = true;
4479
4663
  this.root.add(this.liveButton);
4480
- this.queue = new TextRenderable5(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
4664
+ this.queue = new TextRenderable6(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
4481
4665
  this.root.add(this.queue);
4482
4666
  this.suggestions = new NativeSelectRenderable(renderer2, {
4483
4667
  id: "omnius-suggestions",
@@ -4496,31 +4680,48 @@ var NativeFrontend = class {
4496
4680
  this.suggestions.onActivate = () => this.acceptSuggestion();
4497
4681
  this.suggestions.on("itemSelected", () => this.acceptSuggestion());
4498
4682
  this.root.add(this.suggestions);
4499
- this.status = new BoxRenderable6(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
4683
+ this.status = new BoxRenderable7(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
4500
4684
  this.root.add(this.status);
4501
- this.footer = new BoxRenderable6(renderer2, {
4685
+ this.footer = new BoxRenderable7(renderer2, {
4502
4686
  id: "omnius-input-box",
4503
4687
  width: "100%",
4504
4688
  flexShrink: 0,
4505
- border: ["top"],
4506
- borderStyle: "single",
4689
+ border: [],
4507
4690
  borderColor: colors.border,
4508
4691
  flexDirection: "column"
4509
4692
  });
4510
4693
  this.root.add(this.footer);
4511
- this.footer.renderAfter = (buffer) => {
4512
- if (this.footer.height > 0) buffer.drawText(
4513
- formatSignalDivider(this.footer.width),
4514
- this.footer.x,
4515
- this.footer.y,
4516
- this.context.colors.foreground,
4517
- this.context.colors.background,
4518
- TextAttributes7.DIM
4519
- );
4694
+ this.footerRule = new BoxRenderable7(renderer2, {
4695
+ id: "omnius-input-rule",
4696
+ width: "100%",
4697
+ height: 1,
4698
+ minHeight: 1,
4699
+ maxHeight: 1,
4700
+ flexShrink: 0,
4701
+ flexDirection: "row",
4702
+ paddingLeft: 2,
4703
+ shouldFill: false
4704
+ });
4705
+ this.footer.add(this.footerRule);
4706
+ this.tabs = new NativeTabStrip(renderer2, {
4707
+ id: "omnius-agent-tabs",
4708
+ prefix: "agent",
4709
+ colors,
4710
+ activate: (id) => this.emitAction("view", id)
4711
+ });
4712
+ this.tabs.flexGrow = 0;
4713
+ this.tabs.flexShrink = 0;
4714
+ this.tabs.visible = false;
4715
+ this.footerRule.add(this.tabs);
4716
+ this.footerRule.renderBefore = (buffer) => {
4717
+ buffer.drawText(formatSignalDivider(this.footerRule.width, {
4718
+ direction: "bottom",
4719
+ sidebarColumn: this.instrumentDivider.visible ? this.instrumentDivider.x - this.footerRule.x : void 0
4720
+ }), this.footerRule.x, this.footerRule.y, this.context.colors.foreground, this.context.colors.background, TextAttributes8.DIM);
4520
4721
  };
4521
- const inputRow = new BoxRenderable6(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
4722
+ const inputRow = new BoxRenderable7(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
4522
4723
  this.footer.add(inputRow);
4523
- this.prompt = new TextRenderable5(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
4724
+ this.prompt = new TextRenderable6(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
4524
4725
  inputRow.add(this.prompt);
4525
4726
  this.editor = new TextareaRenderable2(renderer2, {
4526
4727
  id: "omnius-input",
@@ -4540,14 +4741,14 @@ var NativeFrontend = class {
4540
4741
  onCursorChange: () => this.editorChanged(),
4541
4742
  onSubmit: () => this.submit()
4542
4743
  });
4543
- this.vimMode = new TextRenderable5(renderer2, {
4744
+ this.vimMode = new TextRenderable6(renderer2, {
4544
4745
  id: "omnius-vim-mode",
4545
4746
  content: "INSERT",
4546
4747
  height: 1,
4547
4748
  maxWidth: 12,
4548
4749
  flexShrink: 0,
4549
4750
  fg: colors.foreground,
4550
- attributes: TextAttributes7.DIM,
4751
+ attributes: TextAttributes8.DIM,
4551
4752
  wrapMode: "none",
4552
4753
  truncate: true,
4553
4754
  visible: false,
@@ -4561,42 +4762,42 @@ var NativeFrontend = class {
4561
4762
  this.renderer.requestRender();
4562
4763
  });
4563
4764
  inputRow.add(this.editor);
4564
- this.enhance = new BoxRenderable6(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
4765
+ this.enhance = new BoxRenderable7(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
4565
4766
  inputRow.add(this.enhance);
4566
- this.activity = new TextRenderable5(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
4767
+ this.activity = new TextRenderable6(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
4567
4768
  this.status.add(this.activity);
4568
- this.media = new BoxRenderable6(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
4569
- this.mediaText = new TextRenderable5(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
4769
+ this.media = new BoxRenderable7(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
4770
+ this.mediaText = new TextRenderable6(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
4570
4771
  this.media.add(this.mediaText);
4571
- this.micTrack = new TextRenderable5(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
4772
+ this.micTrack = new TextRenderable6(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
4572
4773
  this.media.add(this.micTrack);
4573
4774
  this.status.add(this.media);
4574
- this.copyFeedback = new TextRenderable5(renderer2, {
4775
+ this.copyFeedback = new TextRenderable6(renderer2, {
4575
4776
  id: "omnius-copy-feedback",
4576
4777
  content: "",
4577
4778
  height: 1,
4578
4779
  flexShrink: 0,
4579
4780
  fg: colors.foreground,
4580
- attributes: TextAttributes7.DIM,
4781
+ attributes: TextAttributes8.DIM,
4581
4782
  selectable: false,
4582
4783
  visible: false
4583
4784
  });
4584
4785
  this.status.add(this.copyFeedback);
4585
- this.metricsRow = new BoxRenderable6(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
4586
- this.keyHints = new TextRenderable5(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes7.DIM, fg: colors.foreground, selectable: false });
4786
+ this.metricsRow = new BoxRenderable7(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
4787
+ this.keyHints = new TextRenderable6(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes8.DIM, fg: colors.foreground, selectable: false });
4587
4788
  this.metricsRow.add(this.keyHints);
4588
- this.metricsStartActions = new BoxRenderable6(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
4789
+ this.metricsStartActions = new BoxRenderable7(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
4589
4790
  this.metricsRow.add(this.metricsStartActions);
4590
- this.metrics = new TextRenderable5(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
4791
+ this.metrics = new TextRenderable6(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
4591
4792
  this.metricsRow.add(this.metrics);
4592
- this.metricsEndActions = new BoxRenderable6(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
4793
+ this.metricsEndActions = new BoxRenderable7(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
4593
4794
  this.metricsRow.add(this.metricsEndActions);
4594
4795
  this.root.add(this.metricsRow);
4595
4796
  this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
4596
4797
  renderer2.keyInput.on("keypress", this.onKey);
4597
4798
  renderer2.keyInput.on("paste", this.onPaste);
4598
- renderer2.on(CliRenderEvents4.FRAME, this.onFrame);
4599
- renderer2.on(CliRenderEvents4.RESIZE, this.onResize);
4799
+ renderer2.on(CliRenderEvents5.FRAME, this.onFrame);
4800
+ renderer2.on(CliRenderEvents5.RESIZE, this.onResize);
4600
4801
  this.applyTheme(initialTheme);
4601
4802
  this.editor.focus();
4602
4803
  }
@@ -4616,6 +4817,9 @@ var NativeFrontend = class {
4616
4817
  headerPanel;
4617
4818
  routeWarning;
4618
4819
  tabs;
4820
+ sessionRow;
4821
+ sessions;
4822
+ newSession;
4619
4823
  taskPane;
4620
4824
  tasks;
4621
4825
  body;
@@ -4627,6 +4831,7 @@ var NativeFrontend = class {
4627
4831
  queue;
4628
4832
  suggestions;
4629
4833
  footer;
4834
+ footerRule;
4630
4835
  prompt;
4631
4836
  vimMode;
4632
4837
  enhance;
@@ -4646,7 +4851,6 @@ var NativeFrontend = class {
4646
4851
  state;
4647
4852
  themeSignature = "";
4648
4853
  headerSignature = "";
4649
- tabsSignature = "";
4650
4854
  suggestionSignature = "";
4651
4855
  metricsActionsSignature = "";
4652
4856
  changing = false;
@@ -4674,8 +4878,8 @@ var NativeFrontend = class {
4674
4878
  selectionArmed = false;
4675
4879
  update(state, patch) {
4676
4880
  const inputWasEnabled = this.state?.input.enabled;
4677
- const previousView = this.state ? `${this.state.activeViewId}:${this.state.presentation ?? "stack"}` : void 0;
4678
- const currentView = `${state.activeViewId}:${state.presentation ?? "stack"}`;
4881
+ const previousView = this.state ? `${this.state.sessions?.activeId ?? ""}:${this.state.activeViewId}:${this.state.presentation ?? "stack"}` : void 0;
4882
+ const currentView = `${state.sessions?.activeId ?? ""}:${state.activeViewId}:${state.presentation ?? "stack"}`;
4679
4883
  const viewChanged = previousView !== void 0 && previousView !== currentView;
4680
4884
  if (viewChanged) {
4681
4885
  this.viewScroll.set(previousView, { top: this.scroll.scrollTop, live: this.offset === 0 });
@@ -4701,14 +4905,13 @@ var NativeFrontend = class {
4701
4905
  }
4702
4906
  this.routeWarning.content = state.routeWarning ? `! ${state.routeWarning}` : "";
4703
4907
  this.routeWarning.visible = Boolean(state.routeWarning);
4704
- const tabsSignature = JSON.stringify(state.tabs);
4705
- if (tabsSignature !== this.tabsSignature) {
4706
- this.tabsSignature = tabsSignature;
4707
- this.tabs.setOptions(state.tabs.map((tab) => ({ name: fitInstrumentText(plainText(tab.label), this.tabs.getTabWidth() - 2), description: "", value: tab.id })));
4708
- }
4709
- this.tabs.visible = state.tabs.length > 1;
4710
- const activeTab = state.tabs.findIndex((tab) => tab.id === state.activeViewId);
4711
- if (activeTab >= 0 && activeTab !== this.tabs.getSelectedIndex()) this.tabs.setSelectedIndex(activeTab);
4908
+ this.tabs.update(state.tabs, state.activeViewId);
4909
+ this.tabs.visible = state.tabs.some((tab) => tab.id !== "main");
4910
+ this.sessionRow.visible = Boolean(state.sessions);
4911
+ this.sessions.update(state.sessions?.items ?? [], state.sessions?.activeId ?? "", state.sessions?.busy);
4912
+ this.newSession.focusable = !state.sessions?.busy;
4913
+ if (state.sessions?.busy && this.newSession.focused) this.getRenderable(`session:${state.sessions.activeId}`)?.focus();
4914
+ this.newSession.attributes = state.sessions?.busy ? TextAttributes8.DIM : this.newSession.focused ? TextAttributes8.INVERSE : 0;
4712
4915
  this.tasks.replace(state.tasks);
4713
4916
  this.taskPane.visible = state.tasks.length > 0;
4714
4917
  if (patch) {
@@ -4730,7 +4933,7 @@ var NativeFrontend = class {
4730
4933
  this.prompt.content = styledText(state.prompt.trimEnd() || "❯");
4731
4934
  if (!this.editorInitialized) {
4732
4935
  this.editor.setText(state.input.text);
4733
- this.editor.cursorOffset = Math.max(0, Math.min(state.input.text.length, state.input.cursor));
4936
+ this.placeInputCursor(state.input.text, state.input.cursor);
4734
4937
  this.editorInitialized = true;
4735
4938
  }
4736
4939
  this.editor.focusable = state.input.enabled && !this.paused;
@@ -4739,6 +4942,7 @@ var NativeFrontend = class {
4739
4942
  this.metrics.visible = state.metricsVisible;
4740
4943
  this.instruments.update(state);
4741
4944
  this.layoutInstruments();
4945
+ this.layoutTabStrips();
4742
4946
  const metricsActions = JSON.stringify(state.metricsActions ?? []);
4743
4947
  if (metricsActions !== this.metricsActionsSignature) {
4744
4948
  this.metricsActionsSignature = metricsActions;
@@ -4761,8 +4965,8 @@ var NativeFrontend = class {
4761
4965
  for (const child of [...parent.getChildren()]) child.destroy();
4762
4966
  for (const action of actions) {
4763
4967
  const colors = this.context.colors;
4764
- const attributes = action.disabled ? TextAttributes7.DIM : action.active ? TextAttributes7.BOLD | TextAttributes7.UNDERLINE : 0;
4765
- const button = new TextRenderable5(this.renderer, {
4968
+ const attributes = action.disabled ? TextAttributes8.DIM : action.active ? TextAttributes8.BOLD | TextAttributes8.UNDERLINE : 0;
4969
+ const button = new TextRenderable6(this.renderer, {
4766
4970
  id: `${prefix}:${action.id}`,
4767
4971
  content: styledText(action.label),
4768
4972
  flexShrink: 0,
@@ -4785,16 +4989,16 @@ var NativeFrontend = class {
4785
4989
  button.focusable = !action.disabled;
4786
4990
  if (!action.disabled) {
4787
4991
  button.on("focused", () => {
4788
- button.attributes = attributes | TextAttributes7.INVERSE;
4992
+ button.attributes = attributes | TextAttributes8.INVERSE;
4789
4993
  });
4790
4994
  button.on("blurred", () => {
4791
4995
  button.attributes = attributes;
4792
4996
  });
4793
4997
  button.onMouseOver = () => {
4794
- button.attributes = attributes | TextAttributes7.UNDERLINE | (button.focused ? TextAttributes7.INVERSE : 0);
4998
+ button.attributes = attributes | TextAttributes8.UNDERLINE | (button.focused ? TextAttributes8.INVERSE : 0);
4795
4999
  };
4796
5000
  button.onMouseOut = () => {
4797
- button.attributes = attributes | (button.focused ? TextAttributes7.INVERSE : 0);
5001
+ button.attributes = attributes | (button.focused ? TextAttributes8.INVERSE : 0);
4798
5002
  };
4799
5003
  }
4800
5004
  parent.add(button);
@@ -4856,10 +5060,10 @@ var NativeFrontend = class {
4856
5060
  this.editor.backgroundColor = this.editor.focusedBackgroundColor = colors.background;
4857
5061
  this.editor.textColor = this.editor.focusedTextColor = colors.foreground;
4858
5062
  this.editor.editorView.setSelectionColors(void 0, void 0);
4859
- this.tabs.backgroundColor = this.tabs.focusedBackgroundColor = colors.background;
4860
- this.tabs.textColor = colors.dim;
4861
- this.tabs.selectedTextColor = colors.foreground;
4862
- this.tabs.selectedBackgroundColor = colors.selection;
5063
+ this.tabs.applyTheme(colors);
5064
+ this.sessions.applyTheme(colors);
5065
+ this.newSession.fg = colors.foreground;
5066
+ this.newSession.bg = colors.background;
4863
5067
  this.suggestions.backgroundColor = this.suggestions.focusedBackgroundColor = colors.background;
4864
5068
  this.suggestions.textColor = colors.dim;
4865
5069
  this.suggestions.selectedTextColor = colors.foreground;
@@ -4971,6 +5175,7 @@ var NativeFrontend = class {
4971
5175
  }
4972
5176
  const height = Math.max(1, Math.min(8, this.editor.editorView.getTotalVirtualLineCount(), this.renderer.terminalHeight - 8));
4973
5177
  if (height !== this.editor.height) this.editor.height = height;
5178
+ this.layoutTabStrips();
4974
5179
  const offset = this.offset;
4975
5180
  if (offset === 0) this.newEntries = 0;
4976
5181
  this.liveButton.visible = offset > 0;
@@ -4985,8 +5190,16 @@ var NativeFrontend = class {
4985
5190
  };
4986
5191
  onResize = () => {
4987
5192
  this.layoutInstruments();
5193
+ this.layoutTabStrips();
4988
5194
  this.emit({ type: "resize", columns: this.renderer.terminalWidth, rows: this.renderer.terminalHeight });
4989
5195
  };
5196
+ layoutTabStrips() {
5197
+ const width = this.renderer.terminalWidth;
5198
+ const junction = this.instrumentDivider.visible && this.instruments.visible ? this.instrumentDivider.x : width - 3;
5199
+ const available = Math.max(0, junction - 6);
5200
+ const desired = Math.min(this.tabs.naturalWidth, available);
5201
+ if (this.tabs.width !== desired) this.tabs.width = desired;
5202
+ }
4990
5203
  editorChanged() {
4991
5204
  if (!this.editor || this.changing || this.destroyed || this.inputEventPending) return;
4992
5205
  this.inputEventPending = true;
@@ -5000,7 +5213,7 @@ var NativeFrontend = class {
5000
5213
  }
5001
5214
  flushLatestInput() {
5002
5215
  const text = this.editor.plainText;
5003
- const cursor = this.editor.cursorOffset;
5216
+ const cursor = this.inputCursor();
5004
5217
  if (text === this.lastInput && cursor === this.lastCursor) return;
5005
5218
  this.lastInput = text;
5006
5219
  this.lastCursor = cursor;
@@ -5043,10 +5256,17 @@ var NativeFrontend = class {
5043
5256
  }
5044
5257
  setInput(text, cursor = text.length) {
5045
5258
  this.editor.replaceText(text);
5046
- this.editor.cursorOffset = Math.max(0, Math.min(text.length, cursor));
5259
+ this.placeInputCursor(text, cursor);
5047
5260
  this.editorInitialized = true;
5048
5261
  this.editorChanged();
5049
5262
  }
5263
+ inputCursor() {
5264
+ return this.editor.getTextRange(0, this.editor.cursorOffset).length;
5265
+ }
5266
+ placeInputCursor(text, cursor) {
5267
+ const index = Math.max(0, Math.min(text.length, cursor));
5268
+ this.editor.cursorOffset = this.editor.editBuffer.measureText(text.slice(0, index));
5269
+ }
5050
5270
  shortcut(name, key) {
5051
5271
  this.flushLatestInput();
5052
5272
  this.emit({ type: "shortcut", name });
@@ -5214,7 +5434,7 @@ var NativeFrontend = class {
5214
5434
  const targets = [];
5215
5435
  const visit = (node) => {
5216
5436
  if (!node.visible) return;
5217
- if (node.focusable && node !== this.scroll && !(node instanceof ScrollBoxRenderable4) && node.height > 0 && node.width > 0) targets.push(node);
5437
+ if (node.focusable && node !== this.scroll && !(node instanceof ScrollBoxRenderable5) && node.height > 0 && node.width > 0) targets.push(node);
5218
5438
  for (const child of node.getChildren()) visit(child);
5219
5439
  };
5220
5440
  visit(this.root);
@@ -5296,7 +5516,7 @@ var NativeFrontend = class {
5296
5516
  break;
5297
5517
  }
5298
5518
  case "input":
5299
- if (command2.expectedText !== void 0 && command2.expectedText !== this.editor.plainText || command2.expectedCursor !== void 0 && command2.expectedCursor !== this.editor.cursorOffset) {
5519
+ if (command2.expectedText !== void 0 && command2.expectedText !== this.editor.plainText || command2.expectedCursor !== void 0 && command2.expectedCursor !== this.inputCursor()) {
5300
5520
  this.flushLatestInput();
5301
5521
  } else this.setInput(command2.text, command2.cursor);
5302
5522
  break;
@@ -5406,8 +5626,8 @@ var NativeFrontend = class {
5406
5626
  this.disposeSelectionContrast();
5407
5627
  this.renderer.keyInput.off("keypress", this.onKey);
5408
5628
  this.renderer.keyInput.off("paste", this.onPaste);
5409
- this.renderer.off(CliRenderEvents4.FRAME, this.onFrame);
5410
- this.renderer.off(CliRenderEvents4.RESIZE, this.onResize);
5629
+ this.renderer.off(CliRenderEvents5.FRAME, this.onFrame);
5630
+ this.renderer.off(CliRenderEvents5.RESIZE, this.onResize);
5411
5631
  this.modals.destroy();
5412
5632
  this.content.destroy();
5413
5633
  this.tasks.destroy();
@@ -5418,7 +5638,7 @@ var NativeFrontend = class {
5418
5638
  };
5419
5639
 
5420
5640
  // packages/cli/src/tui/native/components/cursor.ts
5421
- import { CliRenderEvents as CliRenderEvents5 } from "@opentui/core";
5641
+ import { CliRenderEvents as CliRenderEvents6 } from "@opentui/core";
5422
5642
  function installTerminalCursorDefaults(renderer2, session) {
5423
5643
  const reset = new TextEncoder().encode("\x1B]112\x07");
5424
5644
  let disposed = false;
@@ -5468,11 +5688,11 @@ function installTerminalCursorDefaults(renderer2, session) {
5468
5688
  const dispose = () => {
5469
5689
  if (disposed) return;
5470
5690
  disposed = true;
5471
- renderer2.off(CliRenderEvents5.FRAME, afterFrame);
5472
- renderer2.off(CliRenderEvents5.DESTROY, dispose);
5691
+ renderer2.off(CliRenderEvents6.FRAME, afterFrame);
5692
+ renderer2.off(CliRenderEvents6.DESTROY, dispose);
5473
5693
  };
5474
- renderer2.on(CliRenderEvents5.FRAME, afterFrame);
5475
- renderer2.once(CliRenderEvents5.DESTROY, dispose);
5694
+ renderer2.on(CliRenderEvents6.FRAME, afterFrame);
5695
+ renderer2.once(CliRenderEvents6.DESTROY, dispose);
5476
5696
  return { restore, dispose };
5477
5697
  }
5478
5698
 
@@ -5548,10 +5768,10 @@ try {
5548
5768
  maxFps: 60,
5549
5769
  clearOnShutdown: false
5550
5770
  });
5551
- renderer.on(CliRenderEvents6.RENDER_ERROR, (event) => {
5771
+ renderer.on(CliRenderEvents7.RENDER_ERROR, (event) => {
5552
5772
  void fail(event.error);
5553
5773
  });
5554
- renderer.on(CliRenderEvents6.HANDLER_ERROR, (event) => {
5774
+ renderer.on(CliRenderEvents7.HANDLER_ERROR, (event) => {
5555
5775
  void fail(event.error);
5556
5776
  });
5557
5777
  renderer.on("error", (event) => {