priiisk 0.1.21 → 0.1.23

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.
@@ -2,7 +2,6 @@ import { createRequire as __priiiskCreateRequire } from "node:module";
2
2
  const require = __priiiskCreateRequire(import.meta.url);
3
3
  import {
4
4
  CampAskState,
5
- CampWorkerState,
6
5
  get2,
7
6
  make2 as make4,
8
7
  modify,
@@ -11,7 +10,7 @@ import {
11
10
  tagsExhaustive,
12
11
  type,
13
12
  update as update2
14
- } from "./chunk-U2FODXMR.js";
13
+ } from "./chunk-QDMSPX34.js";
15
14
  import {
16
15
  Tag,
17
16
  TaggedError,
@@ -23,7 +22,7 @@ import {
23
22
  catchAll,
24
23
  catchAllCause,
25
24
  currentTimeMillis,
26
- equipmentDescriptionLines,
25
+ equipmentDescriptionGroups,
27
26
  fail4 as fail,
28
27
  flatMap3 as flatMap,
29
28
  forEach,
@@ -56,7 +55,7 @@ import {
56
55
  tap,
57
56
  update,
58
57
  zipRight
59
- } from "./chunk-DPNPQG2D.js";
58
+ } from "./chunk-7OL53RZ7.js";
60
59
  import "./chunk-7UPFIIZD.js";
61
60
 
62
61
  // packages/host-ui/src/backend/campUiBackend.ts
@@ -79,13 +78,30 @@ var CampUiScreen = {
79
78
  camp: "camp",
80
79
  worker: "worker"
81
80
  };
81
+ var CampUiSidebarTab = { workers: "workers", equipment: "equip" };
82
+ var CAMP_UI_SIDEBAR_TABS = [
83
+ CampUiSidebarTab.workers,
84
+ CampUiSidebarTab.equipment
85
+ ];
86
+ var nextCampUiSidebarTab = (current, delta) => {
87
+ const index = CAMP_UI_SIDEBAR_TABS.indexOf(current);
88
+ const size = CAMP_UI_SIDEBAR_TABS.length;
89
+ const next = (((index < 0 ? 0 : index) + delta) % size + size) % size;
90
+ return CAMP_UI_SIDEBAR_TABS[next] ?? CampUiSidebarTab.workers;
91
+ };
82
92
  var campUiAskBlockId = (askId) => `ask:${askId}`;
93
+ var campUiOpenedWorkerId = (state) => state.openedWorkerId;
83
94
  var makeInitialCampUiState = (snapshot, presentationNow = 0) => ({
95
+ /*
96
+ * The cursor starts on the first worker, but nobody is opened: a camp that
97
+ * opened someone by itself answered a question nobody asked and hid what a
98
+ * fresh camp has to say about itself.
99
+ */
84
100
  ...snapshot.workers[0] === void 0 ? {} : { selectedWorkerId: snapshot.workers[0].snapshot.worker.id },
85
101
  screen: CampUiScreen.camp,
86
102
  focusedSlotId: "roster",
87
103
  detailVisible: false,
88
- inventoryVisible: false,
104
+ sidebarTab: CampUiSidebarTab.workers,
89
105
  presentationNow,
90
106
  drafts: /* @__PURE__ */ new Map(),
91
107
  scrollOffsets: /* @__PURE__ */ new Map(),
@@ -110,8 +126,11 @@ var reconcileCampUiState = (current, snapshot) => {
110
126
  );
111
127
  const fallbackIndex = Math.min(Math.max(previousIndex, 0), snapshot.workers.length - 1);
112
128
  const selectedWorkerId = previousId !== void 0 && workerIds.has(previousId) ? previousId : snapshot.workers[fallbackIndex]?.snapshot.worker.id;
129
+ const previousOpenedId = current.state.openedWorkerId;
130
+ const openedWorkerId = previousOpenedId !== void 0 && workerIds.has(previousOpenedId) ? previousOpenedId : void 0;
113
131
  const {
114
132
  selectedWorkerId: _selectedWorkerId,
133
+ openedWorkerId: _openedWorkerId,
115
134
  focusedDetailBlockId: previousFocusedBlockId,
116
135
  ...stableState
117
136
  } = current.state;
@@ -127,6 +146,7 @@ var reconcileCampUiState = (current, snapshot) => {
127
146
  return {
128
147
  ...stableState,
129
148
  ...selectedWorkerId === void 0 ? {} : { selectedWorkerId },
149
+ ...openedWorkerId === void 0 ? {} : { openedWorkerId },
130
150
  ...focusedDetailBlockId === void 0 ? {} : { focusedDetailBlockId },
131
151
  screen: selectedWorkerId === void 0 ? CampUiScreen.camp : current.state.screen,
132
152
  focusedSlotId: selectedWorkerId === void 0 ? "roster" : current.state.focusedSlotId,
@@ -182,12 +202,17 @@ var makeCampUiStore = (initial, presentationNow = 0) => gen(function* () {
182
202
  ...model,
183
203
  state: {
184
204
  ...model.state,
205
+ openedWorkerId: workerId,
185
206
  selectedWorkerId: workerId,
186
207
  screen: CampUiScreen.worker
187
208
  }
188
209
  }
189
210
  ])
190
211
  ),
212
+ closeWorker: updateState((state) => {
213
+ const { openedWorkerId: _openedWorkerId, ...rest } = state;
214
+ return { ...rest, screen: CampUiScreen.camp };
215
+ }),
191
216
  activateScreen: (screen) => modifyEffect(
192
217
  modelRef,
193
218
  (model) => screen === CampUiScreen.worker && model.state.selectedWorkerId === void 0 ? fail(
@@ -211,10 +236,9 @@ var makeCampUiStore = (initial, presentationNow = 0) => gen(function* () {
211
236
  if (state.detailVisible === detailVisible) return state;
212
237
  return { ...state, detailVisible };
213
238
  }),
214
- setInventoryVisible: (inventoryVisible) => updateState((state) => {
215
- if (state.inventoryVisible === inventoryVisible) return state;
216
- return { ...state, inventoryVisible };
217
- }),
239
+ setSidebarTab: (sidebarTab) => updateState(
240
+ (state) => state.sidebarTab === sidebarTab ? state : { ...state, sidebarTab }
241
+ ),
218
242
  setDetailBlockExpanded: (blockId, expanded) => updateState((state) => ({
219
243
  ...state,
220
244
  expandedDetailBlockIds: updateCampUiStringSet(
@@ -289,8 +313,9 @@ var emptyCampUiComponent = {
289
313
  // packages/host-ui/src/composition/campUiSlots.ts
290
314
  var CampUiSlot = {
291
315
  campHeader: "camp-header",
292
- activity: "activity",
293
316
  equipment: "equipment",
317
+ sidebarTabs: "sidebar-tabs",
318
+ overview: "overview",
294
319
  resources: "resources",
295
320
  roster: "roster",
296
321
  sessionHeader: "session-header",
@@ -440,6 +465,7 @@ var makeCampUiIntentHandler = (options) => type().pipe(
440
465
  );
441
466
  }),
442
467
  OpenWorker: ({ workerId }) => options.controller.openWorker(workerId),
468
+ CloseWorker: () => options.controller.closeWorker,
443
469
  ScrollWorker: ({ workerId, delta }) => suspend(() => {
444
470
  const offset = options.readModel().state.scrollOffsets.get(workerId) ?? 0;
445
471
  return options.controller.focusDetailBlock(void 0).pipe(
@@ -461,7 +487,11 @@ var makeCampUiIntentHandler = (options) => type().pipe(
461
487
  }),
462
488
  SelectWorker: ({ workerId }) => options.controller.selectWorker(workerId),
463
489
  SetDraft: ({ workerId, draft }) => options.controller.setDraft(workerId, draft),
464
- SetInventoryVisible: ({ visible }) => options.controller.setInventoryVisible(visible),
490
+ CycleSidebarTab: ({ delta }) => suspend(
491
+ () => options.controller.setSidebarTab(
492
+ nextCampUiSidebarTab(options.readModel().state.sidebarTab, delta)
493
+ )
494
+ ),
465
495
  SetScrollOffset: ({ workerId, offset }) => options.controller.setScrollOffset(workerId, offset),
466
496
  SetTranscriptVisible: ({ visible }) => options.controller.setDetailVisible(visible),
467
497
  Steer: ({ workerId, message }) => message.trim() === "" ? _void : options.backend.steer({ workerId, message }).pipe(
@@ -523,10 +553,10 @@ var findCampUiSpatialFocusTarget = (frame, currentSlotId, focusableSlotIds, dire
523
553
 
524
554
  // packages/host-ui/src/layout/campUiLayoutPolicy.ts
525
555
  var defaultCampUiLayoutPolicy = {
526
- leftMinimumWidth: 28,
527
- rightMinimumWidth: 28,
528
- leftTrackWidthThreshold: 82,
529
- rightTrackWidthThreshold: 112,
556
+ leftMinimumWidth: 44,
557
+ rightMinimumWidth: 44,
558
+ leftTrackWidthThreshold: 90,
559
+ rightTrackWidthThreshold: 136,
530
560
  trackGapWidth: 1,
531
561
  wideMinimumHeight: 18,
532
562
  campHeaderHeight: 1,
@@ -665,7 +695,6 @@ var defaultCampUiLeftSectionSizes = {
665
695
  workers: { kind: CampUiSectionSizeKind.grow, weight: 1, minimumRows: 1 }
666
696
  };
667
697
  var defaultCampUiRightSectionSizes = {
668
- activity: { kind: CampUiSectionSizeKind.grow, weight: 1, minimumRows: 2 },
669
698
  equipment: { kind: CampUiSectionSizeKind.fixed, rows: 7 }
670
699
  };
671
700
 
@@ -685,15 +714,6 @@ var leftSectionHeights = (bodyHeight, slots) => {
685
714
  size(CampUiSlot.roster, defaultCampUiLeftSectionSizes.workers)
686
715
  ]);
687
716
  };
688
- var rightSectionHeights = (bodyHeight, width, slots) => {
689
- const minimumActivityRows = Math.min(units2(bodyHeight), 2);
690
- const fallback = defaultCampUiRightSectionSizes.equipment.rows;
691
- const preferred = units2(
692
- slots?.get(CampUiSlot.equipment)?.preferredRows?.(units2(width)) ?? fallback
693
- );
694
- const equipmentHeight = fixedRows(Math.max(0, bodyHeight - minimumActivityRows), preferred);
695
- return [Math.max(0, bodyHeight - equipmentHeight), equipmentHeight];
696
- };
697
717
  var composerHeight = (availableHeight, width, policy, slots) => {
698
718
  const maximum = units2(policy.composerMaximumHeight);
699
719
  const minimum = Math.min(maximum, units2(policy.composerMinimumHeight));
@@ -706,7 +726,7 @@ var makeNarrowFrame = (viewport, state, policy, slots) => {
706
726
  const footerHeight = fixedRows(viewport.height, policy.footerHeight);
707
727
  const bodyHeight = Math.max(0, viewport.height - footerHeight);
708
728
  const footerY = bodyHeight;
709
- const workerScreen = state.screen === CampUiScreen.worker && state.selectedWorkerId !== void 0;
729
+ const workerScreen = state.screen === CampUiScreen.worker && campUiOpenedWorkerId(state) !== void 0;
710
730
  const [campHeight = 0, rosterHeight = 0] = workerScreen ? [] : leftSectionHeights(bodyHeight, slots);
711
731
  const sessionHeaderHeight = workerScreen ? fixedRows(bodyHeight, policy.sessionHeaderHeight) : 0;
712
732
  const inputHeight = workerScreen ? composerHeight(bodyHeight - sessionHeaderHeight, viewport.width, policy, slots) : 0;
@@ -730,12 +750,12 @@ var makeNarrowFrame = (viewport, state, policy, slots) => {
730
750
  ])
731
751
  };
732
752
  };
733
- var makeWideFrame = (viewport, policy, slots) => {
753
+ var makeWideFrame = (viewport, state, policy, slots) => {
734
754
  const campHeaderHeight = fixedRows(viewport.height, policy.campHeaderHeight);
735
755
  const footerHeight = fixedRows(viewport.height - campHeaderHeight, policy.footerHeight);
736
756
  const bodyHeight = Math.max(0, viewport.height - campHeaderHeight - footerHeight);
737
757
  const footerY = campHeaderHeight + bodyHeight;
738
- const [leftWidth = 0, leftGap = 0, centerWidth = 0, rightGap = 0, rightWidth = 0] = allocateCampUiTrackColumns(viewport.width, [
758
+ const [leftWidth = 0, leftGap = 0, centerWidth = 0] = allocateCampUiTrackColumns(viewport.width, [
739
759
  {
740
760
  kind: CampUiTrackSizeKind.min,
741
761
  minimumColumns: policy.leftMinimumWidth,
@@ -746,57 +766,67 @@ var makeWideFrame = (viewport, policy, slots) => {
746
766
  columns: policy.trackGapWidth,
747
767
  collapseBelow: policy.leftTrackWidthThreshold
748
768
  },
749
- { kind: CampUiTrackSizeKind.fill, weight: 1 },
750
- {
751
- kind: CampUiTrackSizeKind.fixed,
752
- columns: policy.trackGapWidth,
753
- collapseBelow: policy.rightTrackWidthThreshold
754
- },
755
- {
756
- kind: CampUiTrackSizeKind.min,
757
- minimumColumns: policy.rightMinimumWidth,
758
- collapseBelow: policy.rightTrackWidthThreshold
759
- }
769
+ { kind: CampUiTrackSizeKind.fill, weight: 1 }
760
770
  ]);
761
771
  const centerX2 = leftWidth + leftGap;
762
- const rightX = centerX2 + centerWidth + rightGap;
763
- const [activityHeight = 0, equipmentHeight = 0] = rightSectionHeights(
764
- bodyHeight,
765
- rightWidth,
766
- slots
767
- );
768
772
  const sessionHeaderHeight = fixedRows(bodyHeight, policy.sessionHeaderHeight);
769
773
  const inputHeight = composerHeight(bodyHeight - sessionHeaderHeight, centerWidth, policy, slots);
770
774
  const transcriptHeight = bodyHeight - sessionHeaderHeight - inputHeight;
771
775
  const bodyY = campHeaderHeight;
776
+ const opened = campUiOpenedWorkerId(state);
777
+ const sidebarTabsHeight = leftWidth === 0 ? 0 : fixedRows(bodyHeight, 1);
772
778
  return {
773
779
  mode: CampUiLayoutMode.wide,
774
780
  dividers: [],
775
781
  regions: compact([
776
782
  region(CampUiSlot.campHeader, 0, 0, viewport.width, campHeaderHeight),
777
- region(CampUiSlot.roster, 0, bodyY, leftWidth, bodyHeight),
778
- region(CampUiSlot.sessionHeader, centerX2, bodyY, centerWidth, sessionHeaderHeight),
783
+ /*
784
+ * The tab row sits under the track it belongs to and takes one line; the
785
+ * active tab is the content above it.
786
+ */
779
787
  region(
780
- CampUiSlot.transcript,
781
- centerX2,
782
- bodyY + sessionHeaderHeight,
783
- centerWidth,
784
- transcriptHeight
788
+ state.sidebarTab === CampUiSidebarTab.equipment ? CampUiSlot.equipment : CampUiSlot.roster,
789
+ 0,
790
+ bodyY,
791
+ leftWidth,
792
+ Math.max(0, bodyHeight - sidebarTabsHeight)
785
793
  ),
786
794
  region(
787
- CampUiSlot.composer,
788
- centerX2,
789
- bodyY + sessionHeaderHeight + transcriptHeight,
790
- centerWidth,
791
- inputHeight
795
+ CampUiSlot.sidebarTabs,
796
+ 0,
797
+ bodyY + Math.max(0, bodyHeight - sidebarTabsHeight),
798
+ leftWidth,
799
+ sidebarTabsHeight
792
800
  ),
793
- region(CampUiSlot.activity, rightX, bodyY, rightWidth, activityHeight),
794
- region(CampUiSlot.equipment, rightX, bodyY + activityHeight, rightWidth, equipmentHeight),
795
801
  /*
796
- * One line across the whole width: two footers pinned to the corners split
797
- * along the track boundary and repeated what the worker cards already say.
802
+ * With nobody open the middle answers about the camp itself what it
803
+ * loaded and what it can reach instead of showing an empty transcript
804
+ * above an input that has no addressee.
798
805
  */
799
- region(CampUiSlot.footer, 0, footerY, viewport.width, footerHeight)
806
+ ...opened === void 0 ? [region(CampUiSlot.overview, centerX2, bodyY, centerWidth, bodyHeight)] : [
807
+ region(CampUiSlot.sessionHeader, centerX2, bodyY, centerWidth, sessionHeaderHeight),
808
+ region(
809
+ CampUiSlot.transcript,
810
+ centerX2,
811
+ bodyY + sessionHeaderHeight,
812
+ centerWidth,
813
+ transcriptHeight
814
+ ),
815
+ region(
816
+ CampUiSlot.composer,
817
+ centerX2,
818
+ bodyY + sessionHeaderHeight + transcriptHeight,
819
+ centerWidth,
820
+ inputHeight
821
+ )
822
+ ],
823
+ /*
824
+ * The bottom line belongs to the conversation, not to the whole width: it
825
+ * sits under the input and answers about the work happening there. Under
826
+ * the side tracks there is nothing, so the eye is not dragged across the
827
+ * screen by a rule that spans everything.
828
+ */
829
+ region(CampUiSlot.footer, centerX2, footerY, centerWidth, footerHeight)
800
830
  ])
801
831
  };
802
832
  };
@@ -807,7 +837,7 @@ var makeDefaultCampUiLayout = (policy = defaultCampUiLayoutPolicy) => ({
807
837
  width: units2(viewport.width),
808
838
  height: units2(viewport.height)
809
839
  };
810
- return normalized.height >= policy.wideMinimumHeight ? makeWideFrame(normalized, policy, slots) : makeNarrowFrame(normalized, state, policy, slots);
840
+ return normalized.height >= policy.wideMinimumHeight ? makeWideFrame(normalized, state, policy, slots) : makeNarrowFrame(normalized, state, policy, slots);
811
841
  }
812
842
  });
813
843
 
@@ -1231,7 +1261,7 @@ var makeCampUiComposerComponent = (context, tui) => {
1231
1261
  };
1232
1262
  };
1233
1263
  const selectedEditor = () => {
1234
- const workerId = context.readModel().state.selectedWorkerId;
1264
+ const workerId = campUiOpenedWorkerId(context.readModel().state);
1235
1265
  if (workerId === void 0) return void 0;
1236
1266
  const existing = editors.get(workerId);
1237
1267
  if (existing !== void 0) return existing;
@@ -1241,7 +1271,7 @@ var makeCampUiComposerComponent = (context, tui) => {
1241
1271
  };
1242
1272
  const renderSelectedEditor = (width) => {
1243
1273
  const model = context.readModel();
1244
- const workerId = model.state.selectedWorkerId;
1274
+ const workerId = campUiOpenedWorkerId(model.state);
1245
1275
  const entry = selectedEditor();
1246
1276
  if (workerId === void 0 || entry === void 0) return void 0;
1247
1277
  const { editor } = entry;
@@ -1776,7 +1806,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1776
1806
  visibleRanges = [];
1777
1807
  const model = context.readModel();
1778
1808
  const selectTheme = getSelectListTheme8();
1779
- const workerId = model.state.selectedWorkerId;
1809
+ const workerId = campUiOpenedWorkerId(model.state);
1780
1810
  if (workerId === void 0) return [selectTheme.description("No worker selected")];
1781
1811
  const worker = findCampUiWorker(model.snapshot, workerId);
1782
1812
  if (worker === void 0) return [selectTheme.noMatch("Selected worker is unavailable")];
@@ -1856,7 +1886,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1856
1886
  const delta = matchesKey2(input, Key2.up) ? -1 : matchesKey2(input, Key2.down) ? 1 : void 0;
1857
1887
  if (delta === void 0) return false;
1858
1888
  const model = context.readModel();
1859
- const workerId = model.state.selectedWorkerId;
1889
+ const workerId = campUiOpenedWorkerId(model.state);
1860
1890
  if (workerId === void 0) return false;
1861
1891
  const focusedBlockId = model.state.focusedDetailBlockId;
1862
1892
  const current = renderedRanges.findIndex((range) => range.id === focusedBlockId);
@@ -1873,7 +1903,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1873
1903
  return true;
1874
1904
  },
1875
1905
  handlePointer: (pointer) => {
1876
- const workerId = context.readModel().state.selectedWorkerId;
1906
+ const workerId = campUiOpenedWorkerId(context.readModel().state);
1877
1907
  if (workerId === void 0) return [];
1878
1908
  if (pointer.action === "wheel") {
1879
1909
  return [
@@ -1897,169 +1927,9 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1897
1927
  };
1898
1928
  };
1899
1929
 
1900
- // packages/host-ui/src/pi/layout/campUiActivity.ts
1901
- import { getSelectListTheme as getSelectListTheme9 } from "@earendil-works/pi-coding-agent";
1902
- import { sliceByColumn, visibleWidth as visibleWidth3 } from "@earendil-works/pi-tui";
1903
- var CampUiActivityAccess = {
1904
- read: "read",
1905
- edit: "edit",
1906
- write: "write"
1907
- };
1908
- var accessByTool = /* @__PURE__ */ new Map([
1909
- ["read", CampUiActivityAccess.read],
1910
- ["edit", CampUiActivityAccess.edit],
1911
- ["write", CampUiActivityAccess.write]
1912
- ]);
1913
- var accessRank = {
1914
- read: 0,
1915
- edit: 1,
1916
- write: 2
1917
- };
1918
- var filePathArgument = (parameters) => {
1919
- for (const name of ["path", "file_path"]) {
1920
- const value = parameters[name];
1921
- if (typeof value === "string" && value.trim() !== "") return value.trim();
1922
- }
1923
- return void 0;
1924
- };
1925
- var splitPath2 = (path) => {
1926
- const separator = path.lastIndexOf("/");
1927
- if (separator < 0) return { directory: ".", name: path };
1928
- if (separator === 0) return { directory: "/", name: path.slice(1) || path };
1929
- return { directory: path.slice(0, separator), name: path.slice(separator + 1) || path };
1930
- };
1931
- var collectCampUiActivityTree = (transcript) => {
1932
- const directories = /* @__PURE__ */ new Map();
1933
- const nodes = /* @__PURE__ */ new Map();
1934
- for (const message of transcript) {
1935
- for (const content of message.content) {
1936
- if (content.kind !== "tool-call") continue;
1937
- const access = accessByTool.get(content.toolName);
1938
- if (access === void 0) continue;
1939
- const path = filePathArgument(content.arguments);
1940
- if (path === void 0) continue;
1941
- const existing = nodes.get(path);
1942
- if (existing !== void 0) {
1943
- existing.count += 1;
1944
- if (accessRank[access] > accessRank[existing.access]) existing.access = access;
1945
- continue;
1946
- }
1947
- const { directory, name } = splitPath2(path);
1948
- const node = { path, directory, name, access, count: 1 };
1949
- nodes.set(path, node);
1950
- const group = directories.get(directory) ?? { path: directory, nodes: [] };
1951
- if (!directories.has(directory)) directories.set(directory, group);
1952
- group.nodes.push(node);
1953
- }
1954
- }
1955
- return [...directories.values()].map((directory) => ({
1956
- path: directory.path,
1957
- nodes: directory.nodes.map((node) => ({ ...node }))
1958
- }));
1959
- };
1960
- var CAMP_UI_ACTIVITY_MAX_VISIBLE_PATHS = 12;
1961
- var ACTIVITY_COLUMNS_PER_VISIBLE_PATH = 4;
1962
- var flattenedNodes = (tree) => tree.flatMap((directory) => directory.nodes);
1963
- var renderedRowCount = (tree, selected, hidden) => {
1964
- const directories = tree.filter(
1965
- (directory) => directory.nodes.some((node) => selected.has(node))
1966
- ).length;
1967
- return selected.size + directories + (hidden > 0 ? 1 : 0);
1968
- };
1969
- var selectVisibleNodes2 = (tree, width, height) => {
1970
- const nodes = flattenedNodes(tree);
1971
- const selected = /* @__PURE__ */ new Set();
1972
- const widthBudget = Math.max(
1973
- 1,
1974
- Math.floor(Math.max(0, width) / ACTIVITY_COLUMNS_PER_VISIBLE_PATH)
1975
- );
1976
- const nodeBudget = Math.min(CAMP_UI_ACTIVITY_MAX_VISIBLE_PATHS, widthBudget);
1977
- const mutations = nodes.filter((node) => node.access !== CampUiActivityAccess.read);
1978
- const reads = nodes.filter((node) => node.access === CampUiActivityAccess.read);
1979
- const addVisible = (node) => {
1980
- if (selected.size >= nodeBudget) return;
1981
- const candidate = new Set(selected).add(node);
1982
- const hidden = nodes.length - candidate.size;
1983
- if (renderedRowCount(tree, candidate, hidden) <= Math.max(0, height)) selected.add(node);
1984
- };
1985
- for (const node of mutations) addVisible(node);
1986
- if (mutations.some((node) => !selected.has(node))) return selected;
1987
- for (const node of reads) addVisible(node);
1988
- return selected;
1989
- };
1990
- var shortenTail = (value, width) => {
1991
- const available = Math.max(0, Math.floor(width));
1992
- const valueWidth = visibleWidth3(value);
1993
- if (valueWidth <= available) return value;
1994
- if (available <= 0) return "";
1995
- if (available === 1) return "\u2026";
1996
- return `\u2026${sliceByColumn(value, valueWidth - available + 1, available - 1, true)}`;
1997
- };
1998
- var accessPresentation = {
1999
- read: { label: "read", glyph: "r", tone: CampUiTone.neutral },
2000
- edit: { label: "edited", glyph: "~", tone: CampUiTone.attention },
2001
- write: { label: "created", glyph: "+", tone: CampUiTone.success }
2002
- };
2003
- var nodeLine2 = (node, last, width) => {
2004
- const theme = getSelectListTheme9();
2005
- const presentation = accessPresentation[node.access];
2006
- const count = node.count > 1 ? ` \xD7${String(node.count)}` : "";
2007
- const suffix = ` \xB7 ${presentation.label}${count}`;
2008
- const branch = last ? "\u2514\u2500" : "\u251C\u2500";
2009
- const nameWidth = Math.max(1, width - visibleWidth3(`${branch} ${presentation.glyph} ${suffix}`));
2010
- const name = shortenTail(node.name, nameWidth);
2011
- return fitCampUiLine(
2012
- `${theme.description(branch)} ${renderCampUiStatus(name, presentation.tone, presentation.glyph)}${theme.description(suffix)}`,
2013
- width
2014
- );
2015
- };
2016
- var hiddenSummary = (nodes) => {
2017
- const parts = Object.values(CampUiActivityAccess).flatMap((access) => {
2018
- const count = nodes.filter((node) => node.access === access).length;
2019
- if (count === 0) return [];
2020
- const label = accessPresentation[access].label;
2021
- return [`${String(count)} ${label} ${count === 1 ? "path" : "paths"}`];
2022
- });
2023
- return `... ${parts.join(" \xB7 ")} hidden`;
2024
- };
2025
- var renderCampUiActivityTree = (transcript, width, height) => {
2026
- const theme = getSelectListTheme9();
2027
- const tree = collectCampUiActivityTree(transcript);
2028
- const nodes = flattenedNodes(tree);
2029
- if (height <= 0) return [];
2030
- if (nodes.length === 0) return [fitCampUiLine(theme.description("no file activity"), width)];
2031
- const selected = selectVisibleNodes2(tree, width, height);
2032
- const lines = [];
2033
- for (const directory of tree) {
2034
- const visible = directory.nodes.filter((node) => selected.has(node));
2035
- if (visible.length === 0) continue;
2036
- lines.push(fitCampUiLine(theme.description(shortenTail(directory.path, width)), width));
2037
- visible.forEach((node, index) => {
2038
- lines.push(nodeLine2(node, index === visible.length - 1, width));
2039
- });
2040
- }
2041
- const hidden = nodes.filter((node) => !selected.has(node));
2042
- if (hidden.length > 0) {
2043
- lines.push(fitCampUiLine(theme.description(hiddenSummary(hidden)), width));
2044
- }
2045
- return lines;
2046
- };
2047
- var makeCampUiActivity = (context) => ({
2048
- render: (viewport) => {
2049
- const model = context.readModel();
2050
- const workerId = model.state.selectedWorkerId;
2051
- const worker = workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
2052
- return renderCampUiActivityTree(
2053
- worker?.snapshot.session?.transcript ?? [],
2054
- viewport.width,
2055
- viewport.height
2056
- );
2057
- }
2058
- });
2059
-
2060
1930
  // packages/host-ui/src/pi/layout/campUiChrome.ts
2061
- import { getSelectListTheme as getSelectListTheme10 } from "@earendil-works/pi-coding-agent";
2062
- import { truncateToWidth as truncateToWidth4, visibleWidth as visibleWidth4 } from "@earendil-works/pi-tui";
1931
+ import { getSelectListTheme as getSelectListTheme9 } from "@earendil-works/pi-coding-agent";
1932
+ import { truncateToWidth as truncateToWidth4, visibleWidth as visibleWidth3 } from "@earendil-works/pi-tui";
2063
1933
 
2064
1934
  // packages/host-ui/src/pi/shared/campUiPulse.ts
2065
1935
  var BRAILLE_BASE = 10240;
@@ -2151,7 +2021,7 @@ var campUiLevelMarkerText = (level, color) => level === CampUiLevel.resting ? le
2151
2021
  var makeCampUiCampHeader = (context) => ({
2152
2022
  render: (viewport) => {
2153
2023
  const snapshot = context.readModel().snapshot;
2154
- const theme = getSelectListTheme10();
2024
+ const theme = getSelectListTheme9();
2155
2025
  const mode = snapshot.placement === "external" ? "agentless" : "foreman";
2156
2026
  return [
2157
2027
  fitCampUiLine(theme.description(snapshot.displayName), viewport.width),
@@ -2166,13 +2036,13 @@ var PULSE_WINDOW_MS = 12e4;
2166
2036
  var PULSE_CEILING = 4;
2167
2037
  var PULSE_SAMPLES_PER_COLUMN = 2;
2168
2038
  var rosterWidths = (width) => {
2169
- const right = Math.min(17, Math.max(0, width - 17));
2170
- const left = Math.min(11, Math.max(0, width - right - 6));
2171
- return { left, pulse: Math.max(0, width - left - right - 4), right };
2039
+ const right = Math.min(15, Math.max(0, width - 20));
2040
+ const left = Math.min(14, Math.max(0, width - right - 6));
2041
+ return { left, pulse: Math.max(0, width - left - right - 3), right };
2172
2042
  };
2173
2043
  var rightColumn = (text, width) => {
2174
2044
  const fitted = truncateToWidth4(text, width);
2175
- return `${" ".repeat(Math.max(0, width - visibleWidth4(fitted)))}${fitted}`;
2045
+ return `${" ".repeat(Math.max(0, width - visibleWidth3(fitted)))}${fitted}`;
2176
2046
  };
2177
2047
  var pulseSamples = (timestamps, sampleCount, now) => {
2178
2048
  if (sampleCount <= 0) return [];
@@ -2200,23 +2070,24 @@ var usageText = (entry, width) => {
2200
2070
  const usage = entry.snapshot.session?.usage;
2201
2071
  const context = percentText(usage?.context?.percent);
2202
2072
  const cache = usage?.latestCacheHitRate === void 0 ? usage === void 0 || usage.tokens.total === 0 ? "n/a" : "n/r" : percentText(usage.latestCacheHitRate);
2203
- const cost = usage === void 0 ? "\u2014" : `$${usage.cost.toFixed(3)}`;
2204
- const full = `${context} ${cache} ${cost}`;
2205
- if (visibleWidth4(full) <= width || usage === void 0) return full;
2206
- const compactCost = `$${usage.cost.toFixed(2).replace(/^0/u, "")}`;
2207
- return `${context}${cache}${compactCost}`;
2073
+ const full = `${context} cache ${cache}`;
2074
+ return visibleWidth3(full) <= width ? full : `${context} ${cache}`;
2208
2075
  };
2209
2076
  var stateText = (state, duration, width) => {
2210
2077
  const full = `${state} ${duration}`;
2211
- return visibleWidth4(full) <= width ? full : state;
2078
+ return visibleWidth3(full) <= width ? full : state;
2212
2079
  };
2213
- var modelText = (entry) => {
2080
+ var modelText = (entry, width) => {
2214
2081
  const model = entry.snapshot.session?.usage?.model ?? entry.snapshot.worker.equipment.model;
2215
- if (model === void 0) return "model unknown";
2216
- return model.split("/").at(-1) ?? model;
2082
+ const thinking = entry.snapshot.session?.thinkingLevel ?? entry.snapshot.worker.equipment.thinkingLevel;
2083
+ if (model === void 0) return thinking === void 0 ? "model unknown" : thinking;
2084
+ const short = model.split("/").at(-1) ?? model;
2085
+ if (thinking === void 0) return short;
2086
+ const full = `${short} ${thinking}`;
2087
+ return visibleWidth3(full) <= width ? full : short;
2217
2088
  };
2218
2089
  var cardLine = (marker, left, pulse, right, widths, width) => fitCampUiLine(
2219
- `${marker} ${fitCampUiLine(left, widths.left)} ${fitCampUiLine(pulse, widths.pulse)} ${rightColumn(right, widths.right)}`,
2090
+ `${marker} ${fitCampUiLine(left, widths.left)}${fitCampUiLine(pulse, widths.pulse)} ${rightColumn(right, widths.right)}`,
2220
2091
  width
2221
2092
  );
2222
2093
  var renderExternalControllerCard = (controllerConnected, width) => {
@@ -2243,10 +2114,10 @@ var renderExternalControllerCard = (controllerConnected, width) => {
2243
2114
  )
2244
2115
  ];
2245
2116
  };
2246
- var renderWorkerCard = (entry, selectedWorkerId, detailVisible, now, width) => {
2117
+ var renderWorkerCard = (entry, cursorWorkerId, openedWorkerId, now, width) => {
2247
2118
  const worker = entry.snapshot.worker;
2248
2119
  const session = entry.snapshot.session;
2249
- const level = worker.id !== selectedWorkerId ? CampUiLevel.resting : detailVisible ? CampUiLevel.selected : CampUiLevel.focused;
2120
+ const level = worker.id === openedWorkerId ? CampUiLevel.selected : worker.id === cursorWorkerId ? CampUiLevel.focused : CampUiLevel.resting;
2250
2121
  const roleColor = campUiRoleColor(worker.equipment.role);
2251
2122
  const widths = rosterWidths(width);
2252
2123
  const timestamps = (session?.transcript ?? []).flatMap(
@@ -2269,7 +2140,7 @@ var renderWorkerCard = (entry, selectedWorkerId, detailVisible, now, width) => {
2269
2140
  campUiLevelMarkerText(level, roleColor),
2270
2141
  campUiText(name, roleColor, level),
2271
2142
  campUiPulseText(pulse[0], level, roleColor),
2272
- neutral(modelText(entry)),
2143
+ neutral(modelText(entry, widths.right)),
2273
2144
  widths,
2274
2145
  width
2275
2146
  ),
@@ -2289,19 +2160,20 @@ var makeCampUiRoster = (context) => ({
2289
2160
  const model = context.readModel();
2290
2161
  const external = model.snapshot.placement === CampUiPlacement.external;
2291
2162
  if (model.snapshot.workers.length === 0 && !external) {
2292
- return [getSelectListTheme10().description("No workers")];
2163
+ return [getSelectListTheme9().description("No workers")];
2293
2164
  }
2294
2165
  const controller = external ? renderExternalControllerCard(model.snapshot.controllerConnected, viewport.width) : [];
2295
- const workers = model.snapshot.workers.flatMap((entry) => {
2166
+ const workers = model.snapshot.workers.flatMap((entry, index) => {
2296
2167
  const candidateNow = model.state.presentationNow;
2297
2168
  const now = Number.isFinite(candidateNow) ? candidateNow : entry.snapshot.worker.updatedAt;
2298
- return renderWorkerCard(
2169
+ const card = renderWorkerCard(
2299
2170
  entry,
2300
2171
  model.state.selectedWorkerId,
2301
- model.state.detailVisible,
2172
+ campUiOpenedWorkerId(model.state),
2302
2173
  now,
2303
2174
  viewport.width
2304
2175
  );
2176
+ return index === 0 ? card : ["", ...card];
2305
2177
  });
2306
2178
  return [...controller, ...workers];
2307
2179
  },
@@ -2312,7 +2184,8 @@ var makeCampUiRoster = (context) => ({
2312
2184
  if (pointer.y < controllerRows) {
2313
2185
  return [CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster })];
2314
2186
  }
2315
- const workerIndex = Math.floor((pointer.y - controllerRows) / 2);
2187
+ const offset = pointer.y - controllerRows;
2188
+ const workerIndex = offset < 2 ? 0 : Math.floor((offset + 1) / 3);
2316
2189
  const workerId = model.snapshot.workers[workerIndex]?.snapshot.worker.id;
2317
2190
  if (workerId === void 0) return [];
2318
2191
  return [
@@ -2324,8 +2197,8 @@ var makeCampUiRoster = (context) => ({
2324
2197
  var makeCampUiSessionHeader = (context) => ({
2325
2198
  render: (viewport) => {
2326
2199
  const model = context.readModel();
2327
- const theme = getSelectListTheme10();
2328
- const workerId = model.state.selectedWorkerId;
2200
+ const theme = getSelectListTheme9();
2201
+ const workerId = campUiOpenedWorkerId(model.state);
2329
2202
  if (workerId === void 0) {
2330
2203
  return [fitCampUiLine(theme.description("Select a worker"), viewport.width)];
2331
2204
  }
@@ -2346,16 +2219,22 @@ var makeCampUiSessionHeader = (context) => ({
2346
2219
  });
2347
2220
 
2348
2221
  // packages/host-ui/src/pi/layout/campUiEquipment.ts
2349
- import { getSelectListTheme as getSelectListTheme11 } from "@earendil-works/pi-coding-agent";
2222
+ import { getSelectListTheme as getSelectListTheme10 } from "@earendil-works/pi-coding-agent";
2350
2223
  import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
2351
2224
  var fullEquipmentLines = (equipment, revision, width) => {
2352
2225
  if (width <= 0) return [];
2353
- const theme = getSelectListTheme11();
2354
- return equipmentDescriptionLines(equipment, revision).flatMap(
2355
- (line) => wrapTextWithAnsi(theme.description(line), width).map(
2356
- (wrapped) => fitCampUiLine(wrapped, width)
2357
- )
2358
- );
2226
+ const theme = getSelectListTheme10();
2227
+ return equipmentDescriptionGroups(equipment, revision).flatMap((group) => {
2228
+ const values = group.values.length === 0 ? ["none"] : group.values;
2229
+ return [
2230
+ fitCampUiLine(theme.description(group.label), width),
2231
+ ...values.flatMap((value, index) => {
2232
+ const branch = index === values.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
2233
+ const body = `${theme.description(branch)} ${value}`;
2234
+ return wrapTextWithAnsi(body, width).map((wrapped) => fitCampUiLine(wrapped, width));
2235
+ })
2236
+ ];
2237
+ });
2359
2238
  };
2360
2239
  var renderCampUiEquipment = (equipment, revision, width, height) => {
2361
2240
  const lines = fullEquipmentLines(equipment, revision, width);
@@ -2364,7 +2243,7 @@ var renderCampUiEquipment = (equipment, revision, width, height) => {
2364
2243
  if (lines.length <= available) return lines;
2365
2244
  const shown = lines.slice(0, Math.max(0, available - 1));
2366
2245
  const hidden = lines.length - shown.length;
2367
- const summary = getSelectListTheme11().description(
2246
+ const summary = getSelectListTheme10().description(
2368
2247
  `... ${String(hidden)} equipment ${hidden === 1 ? "line" : "lines"} hidden`
2369
2248
  );
2370
2249
  return [...shown, fitCampUiLine(summary, width)];
@@ -2372,18 +2251,20 @@ var renderCampUiEquipment = (equipment, revision, width, height) => {
2372
2251
  var makeCampUiEquipment = (context) => {
2373
2252
  const selectedWorker = () => {
2374
2253
  const model = context.readModel();
2375
- const workerId = model.state.selectedWorkerId;
2254
+ const workerId = campUiOpenedWorkerId(model.state);
2376
2255
  return workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
2377
2256
  };
2378
2257
  const fullLines = (width) => {
2379
2258
  const worker = selectedWorker();
2380
- return worker === void 0 ? [fitCampUiLine(getSelectListTheme11().description("no worker selected"), width)] : fullEquipmentLines(
2259
+ return worker === void 0 ? [fitCampUiLine(getSelectListTheme10().description("no worker selected"), width)] : fullEquipmentLines(
2381
2260
  worker.snapshot.worker.equipment,
2382
2261
  worker.snapshot.worker.equipmentVersion,
2383
2262
  width
2384
2263
  );
2385
2264
  };
2386
2265
  return {
2266
+ /* A tab of the left track takes focus like the roster does. */
2267
+ focusable: true,
2387
2268
  preferredRows: (width) => fullLines(width).length,
2388
2269
  render: (viewport) => {
2389
2270
  const worker = selectedWorker();
@@ -2399,8 +2280,8 @@ var makeCampUiEquipment = (context) => {
2399
2280
  };
2400
2281
 
2401
2282
  // packages/host-ui/src/pi/layout/campUiFooter.ts
2402
- import { getMarkdownTheme as getMarkdownTheme7, getSelectListTheme as getSelectListTheme12 } from "@earendil-works/pi-coding-agent";
2403
- import { sliceByColumn as sliceByColumn2, visibleWidth as visibleWidth5 } from "@earendil-works/pi-tui";
2283
+ import { getMarkdownTheme as getMarkdownTheme7, getSelectListTheme as getSelectListTheme11 } from "@earendil-works/pi-coding-agent";
2284
+ import { sliceByColumn, visibleWidth as visibleWidth4 } from "@earendil-works/pi-tui";
2404
2285
  var footerDivider = (width) => fitCampUiLine(getMarkdownTheme7().codeBlockBorder("\u2500".repeat(width)), width);
2405
2286
  var retryText = (retryAt, attempt, now) => {
2406
2287
  if (retryAt === void 0) return attempt === void 0 ? "" : `attempt ${String(attempt)}`;
@@ -2412,32 +2293,29 @@ var diagnosticText = (value) => {
2412
2293
  const normalized = value?.replaceAll(/\s+/gu, " ").trim();
2413
2294
  return normalized === "" ? void 0 : normalized;
2414
2295
  };
2415
- var workerCountText = (workers) => {
2416
- if (workers.length === 0) return "no workers";
2417
- const working = workers.filter(
2418
- (worker) => worker.snapshot.worker.state === CampWorkerState.running
2419
- ).length;
2420
- const waiting = workers.filter((worker) => worker.asks.some(isPendingAsk)).length;
2421
- const parts = [
2422
- `${String(workers.length)} ${workers.length === 1 ? "worker" : "workers"}`,
2423
- ...working === 0 ? [] : [`${String(working)} working`],
2424
- ...waiting === 0 ? [] : [`${String(waiting)} waiting`]
2425
- ];
2426
- return parts.join(" \xB7 ");
2296
+ var campCostText = (workers) => {
2297
+ const reported = workers.flatMap(
2298
+ (worker) => worker.snapshot.session?.usage === void 0 ? [] : [worker.snapshot.session.usage]
2299
+ );
2300
+ if (workers.length === 0) return "";
2301
+ if (reported.length === 0) return "cost unknown";
2302
+ const total = reported.reduce((sum, usage) => sum + usage.cost, 0);
2303
+ const bound = reported.length < workers.length ? "\u2265" : "";
2304
+ return `${bound}$${total.toFixed(3)}`;
2427
2305
  };
2428
- var isPendingAsk = (ask) => ask.state === "pending";
2306
+ var workerCountText = (workers) => workers.length === 0 ? "no workers" : `${String(workers.length)} ${workers.length === 1 ? "worker" : "workers"}`;
2429
2307
  var shortenPath = (value, width) => {
2430
2308
  const available = Math.max(0, Math.floor(width));
2431
2309
  if (available <= 0) return "";
2432
- if (visibleWidth5(value) <= available) return value;
2310
+ if (visibleWidth4(value) <= available) return value;
2433
2311
  if (available === 1) return "\u2026";
2434
2312
  const kept = available - 1;
2435
- return `\u2026${sliceByColumn2(value, visibleWidth5(value) - kept, kept, true)}`;
2313
+ return `\u2026${sliceByColumn(value, visibleWidth4(value) - kept, kept, true)}`;
2436
2314
  };
2437
2315
  var makeCampUiFooter = (context) => ({
2438
2316
  render: (viewport) => {
2439
2317
  const model = context.readModel();
2440
- const theme = getSelectListTheme12();
2318
+ const theme = getSelectListTheme11();
2441
2319
  const snapshot = model.snapshot;
2442
2320
  const health = snapshot.health;
2443
2321
  const actionError = diagnosticText(model.state.lastActionError);
@@ -2448,24 +2326,27 @@ var makeCampUiFooter = (context) => ({
2448
2326
  const diagnostic = actionError !== void 0 ? `${renderCampUiStatus("action error", CampUiTone.error)} ${theme.description(`\xB7 ${actionError}`)}` : healthMessage !== void 0 ? styleCampUiTone(healthTone, healthMessage) : health.status === CampUiHealthStatus.ready ? "" : renderCampUiStatus(health.status, healthTone);
2449
2327
  const branch = snapshot.projectBranch === null ? "" : ` \xB7 ${snapshot.projectBranch}`;
2450
2328
  const version = `v${snapshot.hostVersion}`;
2329
+ const camp = [workerCountText(snapshot.workers), campCostText(snapshot.workers)].filter(
2330
+ (part) => part !== ""
2331
+ );
2451
2332
  const candidates = [
2452
- { right: [workerCountText(snapshot.workers), version, retry], branch },
2453
- { right: [workerCountText(snapshot.workers), version], branch },
2333
+ { right: [...camp, version, retry], branch },
2334
+ { right: [...camp, version], branch },
2454
2335
  { right: [version], branch },
2455
2336
  { right: [version], branch: "" }
2456
2337
  ];
2457
2338
  const MINIMUM_PATH_COLUMNS = 8;
2458
2339
  const chosen = candidates.find((candidate) => {
2459
- const rightWidth = visibleWidth5(candidate.right.filter((part) => part !== "").join(" \xB7 "));
2460
- const room = viewport.width - rightWidth - 3 - visibleWidth5(candidate.branch);
2340
+ const rightWidth = visibleWidth4(candidate.right.filter((part) => part !== "").join(" \xB7 "));
2341
+ const room = viewport.width - rightWidth - 3 - visibleWidth4(candidate.branch);
2461
2342
  return room >= MINIMUM_PATH_COLUMNS;
2462
2343
  }) ?? candidates[candidates.length - 1];
2463
2344
  const right = (chosen ?? candidates[0])?.right.filter((part) => part !== "").join(" \xB7 ") ?? "";
2464
2345
  const chosenBranch = chosen?.branch ?? "";
2465
- const leftBudget = Math.max(0, viewport.width - visibleWidth5(right) - 3);
2346
+ const leftBudget = Math.max(0, viewport.width - visibleWidth4(right) - 3);
2466
2347
  const path = shortenPath(
2467
2348
  snapshot.projectScope,
2468
- Math.max(0, leftBudget - visibleWidth5(chosenBranch))
2349
+ Math.max(0, leftBudget - visibleWidth4(chosenBranch))
2469
2350
  );
2470
2351
  const left = diagnostic === "" ? theme.description(`${path}${chosenBranch}`) : `${diagnostic} ${theme.description(`\xB7 ${path}${chosenBranch}`)}`;
2471
2352
  return [
@@ -2475,6 +2356,64 @@ var makeCampUiFooter = (context) => ({
2475
2356
  }
2476
2357
  });
2477
2358
 
2359
+ // packages/host-ui/src/pi/layout/campUiOverview.ts
2360
+ import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme12 } from "@earendil-works/pi-coding-agent";
2361
+ var resourceGroup = (label, values, reported, width) => {
2362
+ const theme = getSelectListTheme12();
2363
+ const body = reported ? values.length === 0 ? theme.description("none loaded") : values.join(", ") : theme.description("not reported");
2364
+ return [
2365
+ styleCampUiTone(values.length > 0 ? CampUiTone.attention : CampUiTone.neutral, `[${label}]`),
2366
+ ` ${body}`,
2367
+ ""
2368
+ ].map((line) => fitCampUiLine(line, width));
2369
+ };
2370
+ var mcpNames = (snapshot) => uniqueCampUiNames(
2371
+ snapshot.workers.flatMap((worker) => worker.snapshot.worker.equipment.mcp.serverNames)
2372
+ );
2373
+ var makeCampUiOverview = (context) => ({
2374
+ render: (viewport) => {
2375
+ const model = context.readModel();
2376
+ const snapshot = model.snapshot;
2377
+ const theme = getSelectListTheme12();
2378
+ const dim = getMarkdownTheme8().codeBlockBorder;
2379
+ const inventory = snapshot.resources;
2380
+ const reported = inventory.reportedWorkers > 0;
2381
+ const width = viewport.width;
2382
+ const workers = snapshot.workers.length;
2383
+ const lines = [
2384
+ fitCampUiLine(`${theme.selectedText("priiisk")} ${dim(`v${snapshot.hostVersion}`)}`, width),
2385
+ fitCampUiLine(dim("\u2190 workers \xB7 enter open \xB7 esc back here"), width),
2386
+ fitCampUiLine(
2387
+ dim(
2388
+ workers === 0 ? "\u041B\u0430\u0433\u0435\u0440\u044C \u043F\u0443\u0441\u0442. \u041E\u0440\u043A\u0435\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u043D\u0430\u043D\u0438\u043C\u0430\u0435\u0442 \u0432\u043E\u0440\u043A\u0435\u0440\u043E\u0432 \u0447\u0435\u0440\u0435\u0437 CLI: priiisk hire." : `\u0412 \u043B\u0430\u0433\u0435\u0440\u0435 ${String(workers)} ${workers === 1 ? "\u0432\u043E\u0440\u043A\u0435\u0440" : "\u0432\u043E\u0440\u043A\u0435\u0440\u0430"}. \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0441\u043B\u0435\u0432\u0430 \u0438 \u043E\u0442\u043A\u0440\u043E\u0439\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B.`
2389
+ ),
2390
+ width
2391
+ ),
2392
+ "",
2393
+ ...resourceGroup(
2394
+ "Context",
2395
+ uniqueCampUiNames(inventory.contextFiles.map((item) => item.name)),
2396
+ reported,
2397
+ width
2398
+ ),
2399
+ ...resourceGroup(
2400
+ "Extensions",
2401
+ uniqueCampUiNames(inventory.extensions.map((item) => item.name)),
2402
+ reported,
2403
+ width
2404
+ ),
2405
+ ...resourceGroup(
2406
+ "Skills",
2407
+ uniqueCampUiNames(inventory.skills.map((item) => item.name)),
2408
+ reported,
2409
+ width
2410
+ ),
2411
+ ...resourceGroup("MCP", mcpNames(snapshot), snapshot.workers.length > 0, width)
2412
+ ];
2413
+ return lines.slice(0, Math.max(0, viewport.height));
2414
+ }
2415
+ });
2416
+
2478
2417
  // packages/host-ui/src/pi/layout/campUiResources.ts
2479
2418
  import { getSelectListTheme as getSelectListTheme13 } from "@earendil-works/pi-coding-agent";
2480
2419
  var renderResourceTree = (label, value, tone, width) => {
@@ -2501,7 +2440,7 @@ var makeCampUiResources = (context) => ({
2501
2440
  const value = names.length === 0 ? "none loaded" : names.join(", ");
2502
2441
  return partial ? `${value} \xB7 partial` : value;
2503
2442
  };
2504
- const mcpNames = uniqueCampUiNames(equipment.flatMap((item) => item.mcp.serverNames));
2443
+ const mcpNames2 = uniqueCampUiNames(equipment.flatMap((item) => item.mcp.serverNames));
2505
2444
  const mcpHealth = snapshot.workers.flatMap((worker) => worker.snapshot.mcp);
2506
2445
  const healthNames = new Set(mcpHealth.map((item) => item.serverName));
2507
2446
  const failedMcp = mcpHealth.some(
@@ -2511,15 +2450,15 @@ var makeCampUiResources = (context) => ({
2511
2450
  (item) => item.status === "connecting" || item.status === "retry-wait"
2512
2451
  );
2513
2452
  const partialMcp = mcpHealth.some((item) => !item.catalogComplete);
2514
- const missingMcpHealth = mcpNames.some((name) => !healthNames.has(name));
2515
- const mcpTone = failedMcp ? CampUiTone.error : pendingMcp || partialMcp || missingMcpHealth ? CampUiTone.attention : mcpNames.length > 0 ? CampUiTone.success : CampUiTone.neutral;
2453
+ const missingMcpHealth = mcpNames2.some((name) => !healthNames.has(name));
2454
+ const mcpTone = failedMcp ? CampUiTone.error : pendingMcp || partialMcp || missingMcpHealth ? CampUiTone.attention : mcpNames2.length > 0 ? CampUiTone.success : CampUiTone.neutral;
2516
2455
  const mcpNotes = [
2517
2456
  ...failedMcp ? ["down"] : [],
2518
2457
  ...pendingMcp ? ["connecting"] : [],
2519
2458
  ...partialMcp ? ["catalog partial"] : [],
2520
2459
  ...missingMcpHealth ? ["health not reported"] : []
2521
2460
  ];
2522
- const mcpText = !equipmentReported ? "not reported" : mcpNames.length === 0 ? "none configured" : `${mcpNames.join(", ")}${mcpNotes.length === 0 ? "" : ` \xB7 ${mcpNotes.join(", ")}`}`;
2461
+ const mcpText = !equipmentReported ? "not reported" : mcpNames2.length === 0 ? "none configured" : `${mcpNames2.join(", ")}${mcpNotes.length === 0 ? "" : ` \xB7 ${mcpNotes.join(", ")}`}`;
2523
2462
  const contentWidth = Math.max(1, viewport.width - 2);
2524
2463
  const body = [
2525
2464
  ...renderResourceTree(
@@ -2546,6 +2485,22 @@ var makeCampUiResources = (context) => ({
2546
2485
  }
2547
2486
  });
2548
2487
 
2488
+ // packages/host-ui/src/pi/layout/campUiSidebarTabs.ts
2489
+ import { getMarkdownTheme as getMarkdownTheme9, getSelectListTheme as getSelectListTheme14 } from "@earendil-works/pi-coding-agent";
2490
+ import { visibleWidth as visibleWidth5 } from "@earendil-works/pi-tui";
2491
+ var makeCampUiSidebarTabs = (context) => ({
2492
+ render: (viewport) => {
2493
+ const model = context.readModel();
2494
+ const theme = getSelectListTheme14();
2495
+ const dim = getMarkdownTheme9().codeBlockBorder;
2496
+ const row = CAMP_UI_SIDEBAR_TABS.map(
2497
+ (tab) => tab === model.state.sidebarTab ? theme.selectedText(tab) : dim(tab)
2498
+ ).join(dim("\xB7"));
2499
+ const padding = Math.max(0, Math.floor((viewport.width - visibleWidth5(row)) / 2));
2500
+ return [fitCampUiLine(`${" ".repeat(padding)}${row}`, viewport.width)];
2501
+ }
2502
+ });
2503
+
2549
2504
  // packages/host-ui/src/pi/layout/campUiDefaultSlots.ts
2550
2505
  var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2551
2506
  {
@@ -2553,11 +2508,6 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2553
2508
  slotId: CampUiSlot.campHeader,
2554
2509
  mount: (context) => succeed2(makeCampUiCampHeader(context))
2555
2510
  },
2556
- {
2557
- id: "default-activity",
2558
- slotId: CampUiSlot.activity,
2559
- mount: (context) => succeed2(makeCampUiActivity(context))
2560
- },
2561
2511
  {
2562
2512
  id: "default-equipment",
2563
2513
  slotId: CampUiSlot.equipment,
@@ -2583,6 +2533,16 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2583
2533
  slotId: CampUiSlot.composer,
2584
2534
  mount: (context) => succeed2(makeCampUiComposerComponent(context, tui))
2585
2535
  },
2536
+ {
2537
+ id: "default-overview",
2538
+ slotId: CampUiSlot.overview,
2539
+ mount: (context) => succeed2(makeCampUiOverview(context))
2540
+ },
2541
+ {
2542
+ id: "default-sidebar-tabs",
2543
+ slotId: CampUiSlot.sidebarTabs,
2544
+ mount: (context) => succeed2(makeCampUiSidebarTabs(context))
2545
+ },
2586
2546
  {
2587
2547
  id: "default-footer",
2588
2548
  slotId: CampUiSlot.footer,
@@ -2596,11 +2556,11 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2596
2556
  ];
2597
2557
 
2598
2558
  // packages/host-ui/src/pi/layout/campUiSection.ts
2599
- import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme14 } from "@earendil-works/pi-coding-agent";
2559
+ import { getMarkdownTheme as getMarkdownTheme10, getSelectListTheme as getSelectListTheme15 } from "@earendil-works/pi-coding-agent";
2600
2560
  import { visibleWidth as visibleWidth6 } from "@earendil-works/pi-tui";
2601
2561
  var renderHeader = (header, width) => {
2602
- const theme = getMarkdownTheme8();
2603
- const selectTheme = getSelectListTheme14();
2562
+ const theme = getMarkdownTheme10();
2563
+ const selectTheme = getSelectListTheme15();
2604
2564
  const rule2 = header.focused === true ? selectTheme.selectedPrefix : theme.codeBlockBorder;
2605
2565
  const title = header.focused === true ? selectTheme.selectedText : theme.heading;
2606
2566
  const ruleCharacter = header.focused === true ? "\u2501" : "\u2500";
@@ -2631,14 +2591,24 @@ var makeCampUiSection = (options) => {
2631
2591
  options.children.map((child) => child.size)
2632
2592
  )
2633
2593
  ];
2594
+ const body = options.children.flatMap((child, index) => {
2595
+ const height = childHeights[index] ?? 0;
2596
+ const lines = child.component.render({ width: viewport.width, height }).slice(0, height);
2597
+ return options.centerContent === true ? lines : [...lines, ...Array.from({ length: height - lines.length }, () => "")];
2598
+ });
2599
+ if (options.centerContent !== true) {
2600
+ return [renderHeader(options.header(), viewport.width), ...body];
2601
+ }
2602
+ const padding = Math.max(0, Math.floor((viewport.height - body.length - 1) / 2));
2634
2603
  return [
2604
+ ...Array.from({ length: padding }, () => ""),
2635
2605
  renderHeader(options.header(), viewport.width),
2636
- ...options.children.flatMap((child, index) => {
2637
- const height = childHeights[index] ?? 0;
2638
- const lines = child.component.render({ width: viewport.width, height }).slice(0, height);
2639
- return [...lines, ...Array.from({ length: height - lines.length }, () => "")];
2640
- })
2641
- ];
2606
+ ...body,
2607
+ ...Array.from(
2608
+ { length: Math.max(0, viewport.height - padding - body.length - 1) },
2609
+ () => ""
2610
+ )
2611
+ ].slice(0, viewport.height);
2642
2612
  },
2643
2613
  ...inputComponents ? {
2644
2614
  handleInput: (input) => [...components].reverse().some((component) => component.handleInput?.(input) === true)
@@ -2688,25 +2658,24 @@ var decorateDefaultCampUiSections = (context, slots) => {
2688
2658
  ]
2689
2659
  })
2690
2660
  );
2691
- decorated.set(
2692
- CampUiSlot.activity,
2693
- makeCampUiSection({
2694
- header: () => ({ title: "Activity" }),
2695
- size: defaultCampUiRightSectionSizes.activity,
2696
- children: [
2697
- {
2698
- component: component(CampUiSlot.activity),
2699
- size: { kind: CampUiSectionSizeKind.grow, weight: 1 }
2700
- }
2701
- ]
2702
- })
2703
- );
2704
2661
  const equipment = component(CampUiSlot.equipment);
2705
2662
  decorated.set(
2706
2663
  CampUiSlot.equipment,
2707
2664
  makeCampUiSection({
2708
- header: () => ({ title: "Equipment" }),
2709
- size: defaultCampUiRightSectionSizes.equipment,
2665
+ /*
2666
+ * The worker's name lives in the section header instead of being repeated
2667
+ * inside: one line saying whose grant this is beats the same name printed
2668
+ * twice within a track that is narrow to begin with.
2669
+ */
2670
+ header: () => {
2671
+ const model = context.readModel();
2672
+ const workerId = campUiOpenedWorkerId(model.state);
2673
+ const worker = workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
2674
+ const name = worker?.snapshot.worker.alias ?? workerId?.slice(0, 8);
2675
+ return name === void 0 ? { title: "Equipment" } : { title: "Equipment", detail: name };
2676
+ },
2677
+ centerContent: true,
2678
+ size: defaultCampUiLeftSectionSizes.workers,
2710
2679
  children: [
2711
2680
  {
2712
2681
  component: equipment,
@@ -2724,6 +2693,7 @@ var decorateDefaultCampUiSections = (context, slots) => {
2724
2693
  detail: String(context.readModel().snapshot.workers.length),
2725
2694
  focused: context.readModel().state.focusedSlotId === CampUiSlot.roster
2726
2695
  }),
2696
+ centerContent: true,
2727
2697
  size: defaultCampUiLeftSectionSizes.workers,
2728
2698
  children: [
2729
2699
  {
@@ -2741,7 +2711,7 @@ import { initTheme } from "@earendil-works/pi-coding-agent";
2741
2711
  import { ProcessTerminal, TUI } from "@earendil-works/pi-tui";
2742
2712
 
2743
2713
  // packages/host-ui/src/pi/piCampUiRoot.ts
2744
- import { getMarkdownTheme as getMarkdownTheme9 } from "@earendil-works/pi-coding-agent";
2714
+ import { getMarkdownTheme as getMarkdownTheme11 } from "@earendil-works/pi-coding-agent";
2745
2715
  import { Key as Key3, matchesKey as matchesKey3 } from "@earendil-works/pi-tui";
2746
2716
 
2747
2717
  // packages/host-ui/src/pi/runtime/campUiPointerInput.ts
@@ -2870,7 +2840,7 @@ var PiCampUiRoot = class {
2870
2840
  segments.push({
2871
2841
  x: divider.x,
2872
2842
  width: 1,
2873
- text: getMarkdownTheme9().codeBlockBorder("\u2502")
2843
+ text: getMarkdownTheme11().codeBlockBorder("\u2502")
2874
2844
  });
2875
2845
  }
2876
2846
  }
@@ -2917,19 +2887,19 @@ var PiCampUiRoot = class {
2917
2887
  if (focusedSlotId !== model.state.focusedSlotId) {
2918
2888
  this.context.emit(CampUiIntents.FocusSlot({ slotId: focusedSlotId }));
2919
2889
  }
2920
- if (model.state.inventoryVisible && matchesKey3(input, Key3.escape)) {
2921
- this.context.emit(CampUiIntents.SetInventoryVisible({ visible: false }));
2922
- return;
2923
- }
2924
- if (focusedSlotId !== CampUiSlot.composer && input === "i") {
2925
- this.context.emit(CampUiIntents.SetInventoryVisible({ visible: true }));
2926
- return;
2927
- }
2928
2890
  if (this.slots.get(focusedSlotId)?.handleInput?.(input) === true) return;
2929
2891
  if (matchesKey3(input, Key3.escape)) {
2930
2892
  if (focusedSlotId === CampUiSlot.composer) {
2931
2893
  this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2932
- } else if (model.state.screen === CampUiScreen.worker) {
2894
+ return;
2895
+ }
2896
+ if (model.state.openedWorkerId !== void 0) {
2897
+ this.context.emit(CampUiIntents.CloseWorker());
2898
+ this.context.emit(CampUiIntents.FocusDetailBlock({}));
2899
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster }));
2900
+ return;
2901
+ }
2902
+ if (model.state.screen === CampUiScreen.worker) {
2933
2903
  this.context.emit(CampUiIntents.ActivateScreen({ screen: CampUiScreen.camp }));
2934
2904
  this.context.emit(CampUiIntents.FocusDetailBlock({}));
2935
2905
  this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster }));
@@ -2956,6 +2926,16 @@ var PiCampUiRoot = class {
2956
2926
  );
2957
2927
  return;
2958
2928
  }
2929
+ if (focusedSlotId === CampUiSlot.roster || focusedSlotId === CampUiSlot.equipment) {
2930
+ if (matchesKey3(input, Key3.left)) {
2931
+ this.context.emit(CampUiIntents.CycleSidebarTab({ delta: 1 }));
2932
+ return;
2933
+ }
2934
+ if (matchesKey3(input, Key3.right)) {
2935
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2936
+ return;
2937
+ }
2938
+ }
2959
2939
  if (focusedSlotId === CampUiSlot.roster) {
2960
2940
  const delta = matchesKey3(input, Key3.up) ? -1 : matchesKey3(input, Key3.down) ? 1 : void 0;
2961
2941
  if (delta !== void 0) {
@@ -2970,11 +2950,9 @@ var PiCampUiRoot = class {
2970
2950
  }
2971
2951
  if (matchesKey3(input, Key3.enter) && model.state.selectedWorkerId !== void 0) {
2972
2952
  this.context.emit(CampUiIntents.OpenWorker({ workerId: model.state.selectedWorkerId }));
2973
- this.context.emit(
2974
- CampUiIntents.FocusSlot({
2975
- slotId: this.mode === CampUiLayoutMode.wide ? CampUiSlot.composer : CampUiSlot.transcript
2976
- })
2977
- );
2953
+ if (this.mode !== CampUiLayoutMode.wide) {
2954
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2955
+ }
2978
2956
  return;
2979
2957
  }
2980
2958
  }
@@ -3188,22 +3166,6 @@ var mountPiCampUi = (options) => gen(function* () {
3188
3166
  );
3189
3167
  tui.addChild(root);
3190
3168
  tui.setFocus(root);
3191
- const inventory = slots.get(CampUiSlot.overlay);
3192
- const inventoryOverlay = inventory === void 0 ? void 0 : tui.showOverlay(
3193
- {
3194
- render: (width) => [
3195
- ...inventory.render({ width, height: Math.max(0, terminal.rows) })
3196
- ],
3197
- invalidate: () => inventory.invalidate?.()
3198
- },
3199
- {
3200
- width: "60%",
3201
- minWidth: 36,
3202
- margin: 1,
3203
- visible: () => current.state.inventoryVisible,
3204
- nonCapturing: true
3205
- }
3206
- );
3207
3169
  yield* sync(() => {
3208
3170
  terminal.setTitle(initial.displayName);
3209
3171
  terminal.clearScreen();
@@ -3220,9 +3182,6 @@ var mountPiCampUi = (options) => gen(function* () {
3220
3182
  )
3221
3183
  )
3222
3184
  );
3223
- if (inventoryOverlay !== void 0) {
3224
- yield* addFinalizer(() => sync(() => inventoryOverlay.hide()));
3225
- }
3226
3185
  yield* forkScoped(
3227
3186
  gen(function* () {
3228
3187
  for (let attempt = 0; attempt < 25; attempt += 1) {
@@ -3300,7 +3259,7 @@ var mountPiCampUi = (options) => gen(function* () {
3300
3259
  });
3301
3260
 
3302
3261
  // packages/host-ui/src/pi/run-selector/campRunSelector.ts
3303
- import { getMarkdownTheme as getMarkdownTheme10, getSelectListTheme as getSelectListTheme15, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
3262
+ import { getMarkdownTheme as getMarkdownTheme12, getSelectListTheme as getSelectListTheme16, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
3304
3263
  import {
3305
3264
  Key as Key4,
3306
3265
  matchesKey as matchesKey4,
@@ -3322,8 +3281,8 @@ var PiCampRunSelectorRoot = class {
3322
3281
  render(width) {
3323
3282
  const height = Math.max(0, this.getHeight());
3324
3283
  const viewportWidth = Math.max(0, width - 1);
3325
- const markdown = getMarkdownTheme10();
3326
- const selection = getSelectListTheme15();
3284
+ const markdown = getMarkdownTheme12();
3285
+ const selection = getSelectListTheme16();
3327
3286
  const header = `${markdown.codeBlockBorder("\u2500\u2500")} ${markdown.heading("Resume camp")} ${markdown.codeBlockBorder("\u2500".repeat(Math.max(0, viewportWidth - 16)))}`;
3328
3287
  const available = Math.max(0, height - 2);
3329
3288
  const capacity = Math.max(1, Math.floor(available / 2));
@@ -3401,15 +3360,14 @@ var selectCampRun = (options) => scoped(
3401
3360
  // packages/host-ui/src/index.ts
3402
3361
  var PACKAGE = "@priiisk/host-ui";
3403
3362
  export {
3404
- CAMP_UI_ACTIVITY_MAX_VISIBLE_PATHS,
3405
3363
  CAMP_UI_BASH_PREVIEW_LINES,
3406
3364
  CAMP_UI_DEFAULT_TOOL_PREVIEW_LINES,
3407
3365
  CAMP_UI_EXPLORED_COLLAPSED_PATHS,
3408
3366
  CAMP_UI_EXPLORED_TOOL_NAMES,
3409
3367
  CAMP_UI_REDUCED_MOTION_ENV,
3368
+ CAMP_UI_SIDEBAR_TABS,
3410
3369
  CAMP_UI_SUBSCRIPTION_BASE_DELAY_MS,
3411
3370
  CAMP_UI_SUBSCRIPTION_MAX_RETRIES,
3412
- CampUiActivityAccess,
3413
3371
  CampUiAssistantMessage,
3414
3372
  CampUiBackend,
3415
3373
  CampUiBackendError,
@@ -3426,6 +3384,7 @@ export {
3426
3384
  CampUiRuntimeStatus,
3427
3385
  CampUiScreen,
3428
3386
  CampUiSectionSizeKind,
3387
+ CampUiSidebarTab,
3429
3388
  CampUiSlot,
3430
3389
  CampUiTone,
3431
3390
  CampUiTrackSizeKind,
@@ -3442,6 +3401,7 @@ export {
3442
3401
  campUiExploredReadPath,
3443
3402
  campUiExploredReadRange,
3444
3403
  campUiMessageSignature,
3404
+ campUiOpenedWorkerId,
3445
3405
  campUiPendingAskBlockRenderer,
3446
3406
  campUiPendingAskTargets,
3447
3407
  campUiReducedMotionFromEnv,
@@ -3450,7 +3410,6 @@ export {
3450
3410
  campUiToolResultCacheSize,
3451
3411
  campUiTranscriptBlockRenderer,
3452
3412
  campUiWorkerStateLabel,
3453
- collectCampUiActivityTree,
3454
3413
  compactNumber,
3455
3414
  composeCampUiDetailBlocks,
3456
3415
  composeCampUiSlots,
@@ -3471,7 +3430,6 @@ export {
3471
3430
  isCampUiPendingAsk,
3472
3431
  joinCampUiColumns,
3473
3432
  jsonText,
3474
- makeCampUiActivity,
3475
3433
  makeCampUiComponentGroup,
3476
3434
  makeCampUiComposerComponent,
3477
3435
  makeCampUiDetailComponent,
@@ -3480,15 +3438,17 @@ export {
3480
3438
  makeCampUiIntentHandler,
3481
3439
  makeCampUiIntentRouter,
3482
3440
  makeCampUiModel,
3441
+ makeCampUiOverview,
3483
3442
  makeCampUiResources,
3484
3443
  makeCampUiSection,
3444
+ makeCampUiSidebarTabs,
3485
3445
  makeCampUiStore,
3486
3446
  makeDefaultCampUiContributions,
3487
3447
  makeDefaultCampUiLayout,
3488
3448
  makeInitialCampUiState,
3489
3449
  mountPiCampUi,
3490
3450
  nextCampUiPendingAskTarget,
3491
- renderCampUiActivityTree,
3451
+ nextCampUiSidebarTab,
3492
3452
  renderCampUiDetailBlockLayout,
3493
3453
  renderCampUiDetailBlocks,
3494
3454
  renderCampUiDirectedBlock,