codexmate 0.1.19 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexmate",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -732,8 +732,8 @@
732
732
  this.invalidateSessionTimelineMeasurementCache();
733
733
  },
734
734
 
735
- async loadMoreSessionMessages() {
736
- return loadMoreSessionMessagesHelper.call(this);
735
+ async loadMoreSessionMessages(stepSize) {
736
+ return loadMoreSessionMessagesHelper.call(this, stepSize);
737
737
  },
738
738
 
739
739
  suspendSessionTabRender() {
@@ -55,6 +55,7 @@ export function createPiConfigMethods({ api: apiClient }) {
55
55
  : '';
56
56
  const updates = { defaultProvider: providerId };
57
57
  if (firstModel) updates.defaultModel = firstModel;
58
+ else updates.defaultModel = '';
58
59
  try {
59
60
  const result = await apiClient('write-pi-settings', updates);
60
61
  if (result && result.error) throw new Error(result.error);
@@ -126,6 +127,11 @@ export function createPiConfigMethods({ api: apiClient }) {
126
127
  this.messageType = 'error';
127
128
  return;
128
129
  }
130
+ if (!Array.isArray(merged.models) || merged.models.length === 0) {
131
+ this.message = '请至少导入或输入一个模型';
132
+ this.messageType = 'error';
133
+ return;
134
+ }
129
135
  const providerId = isNew ? this.derivePiProviderId(baseUrl) : editor.id;
130
136
  if (isNew) {
131
137
  merged.id = providerId;
@@ -207,6 +213,7 @@ export function createPiConfigMethods({ api: apiClient }) {
207
213
  startPiProviderDraft(tpl) {
208
214
  const template = isPiPlainObject(tpl) ? tpl : {};
209
215
  this.resetPiEditorTransientState();
216
+ this.showAddPiProviderModal = false;
210
217
  this.editingPiProvider = {
211
218
  id: '',
212
219
  isNew: true,
@@ -350,6 +357,8 @@ export function createPiConfigMethods({ api: apiClient }) {
350
357
  const wasNew = !!editor.isNew;
351
358
  this.resetPiProviderEditing();
352
359
  if (wasNew) this.piSelectedProviderTemplate = '';
360
+ this.showAddPiProviderModal = false;
361
+ this.piProviderPickerQuery = '';
353
362
  },
354
363
  removePiProviderModel(index) {
355
364
  if (!this.editingPiProvider) return;
@@ -544,7 +553,7 @@ export function createPiConfigMethods({ api: apiClient }) {
544
553
  if (merged.baseUrl === undefined) merged.baseUrl = values.baseUrl || '';
545
554
  if (merged.api === undefined) merged.api = values.api || '';
546
555
  if (merged.apiKey === undefined) merged.apiKey = values.apiKey || '';
547
- if (merged.models === undefined) merged.models = mapModels(values.models);
556
+ merged.models = mapModels(merged.models !== undefined ? merged.models : values.models);
548
557
  if (isPiPlainObject(orig.headers)) merged.headers = orig.headers;
549
558
  if (typeof orig.title === 'string') merged.title = orig.title;
550
559
  else if (typeof orig.name === 'string') merged.name = orig.name;
@@ -1362,9 +1362,9 @@ const en = Object.freeze({
1362
1362
  'pi.providers.json.previewTitle': 'Preview',
1363
1363
  'pi.providers.json.editTitle': 'Edit JSON',
1364
1364
  'pi.files.title': 'File JSON',
1365
- 'pi.files.desc': 'View and edit Pi settings.json and models.json in full; JSON validity is checked before saving and invalid content is never written.',
1366
1365
  'pi.files.settingsTitle': 'settings.json (active settings)',
1367
1366
  'pi.files.modelsTitle': 'models.json (providers and models)',
1367
+ 'pi.files.modelsHint': 'Merged automatically from the provider management above; prefer editing providers there instead.',
1368
1368
  'pi.history.apply': 'Apply this record',
1369
1369
  'pi.history.applied': 'History record applied',
1370
1370
  'pi.history.applyFailed': 'Failed to apply history record',
@@ -1363,9 +1363,9 @@ const ja = Object.freeze({
1363
1363
  'pi.providers.json.previewTitle': 'プレビュー',
1364
1364
  'pi.providers.json.editTitle': 'JSON を編集',
1365
1365
  'pi.files.title': 'ファイル JSON',
1366
- 'pi.files.desc': 'Pi の settings.json と models.json を全文表示して直接編集します。保存前に JSON の正当性を検証し、不正な内容は書き込みません。',
1367
1366
  'pi.files.settingsTitle': 'settings.json(有効な設定)',
1368
1367
  'pi.files.modelsTitle': 'models.json(プロバイダーとモデル)',
1368
+ 'pi.files.modelsHint': '上のプロバイダー管理から自動的にマージされます。プロバイダーの変更は上の管理画面を優先してください。',
1369
1369
  'pi.history.apply': 'この履歴を適用',
1370
1370
  'pi.history.applied': '履歴を適用しました',
1371
1371
  'pi.history.applyFailed': '履歴の適用に失敗しました',
@@ -1331,9 +1331,9 @@ const vi = Object.freeze({
1331
1331
  'pi.providers.json.previewTitle': 'Xem trước',
1332
1332
  'pi.providers.json.editTitle': 'Chỉnh sửa JSON',
1333
1333
  'pi.files.title': 'JSON tập tin',
1334
- 'pi.files.desc': 'Hiển thị đầy đủ và chỉnh sửa trực tiếp settings.json và models.json của Pi; kiểm tra tính hợp lệ của JSON trước khi lưu, nội dung không hợp lệ sẽ không được ghi.',
1335
1334
  'pi.files.settingsTitle': 'settings.json (cài đặt đang dùng)',
1336
1335
  'pi.files.modelsTitle': 'models.json (nhà cung cấp và mô hình)',
1336
+ 'pi.files.modelsHint': 'Được hợp nhất tự động từ phần quản lý nhà cung cấp phía trên; hãy ưu tiên chỉnh sửa ở đó.',
1337
1337
  'pi.history.apply': 'Áp dụng bản ghi này',
1338
1338
  'pi.history.applied': 'Đã áp dụng lịch sử',
1339
1339
  'pi.history.applyFailed': 'Áp dụng lịch sử thất bại',
@@ -1360,9 +1360,9 @@ const zhTw = Object.freeze({
1360
1360
  'pi.providers.json.previewTitle': '預覽',
1361
1361
  'pi.providers.json.editTitle': '編輯 JSON',
1362
1362
  'pi.files.title': '檔案 JSON',
1363
- 'pi.files.desc': '完整展示並直接編輯 Pi 的 settings.json 與 models.json;儲存前校驗 JSON 合法性,非法內容不會寫入。',
1364
1363
  'pi.files.settingsTitle': 'settings.json(生效設定)',
1365
1364
  'pi.files.modelsTitle': 'models.json(供應商與模型)',
1365
+ 'pi.files.modelsHint': '由上方供應商管理自動合併生成;調整供應商請優先使用上方的供應商管理。',
1366
1366
  'pi.history.apply': '套用此紀錄',
1367
1367
  'pi.history.applied': '已套用歷史紀錄',
1368
1368
  'pi.history.applyFailed': '套用歷史紀錄失敗',
@@ -1360,9 +1360,9 @@ const zh = Object.freeze({
1360
1360
  'pi.providers.json.previewTitle': '预览',
1361
1361
  'pi.providers.json.editTitle': '编辑 JSON',
1362
1362
  'pi.files.title': '文件 JSON',
1363
- 'pi.files.desc': '完整展示并直接编辑 Pi 的 settings.json 与 models.json;保存前校验 JSON 合法性,非法内容不会写入。',
1364
1363
  'pi.files.settingsTitle': 'settings.json(生效设置)',
1365
1364
  'pi.files.modelsTitle': 'models.json(供应商与模型)',
1365
+ 'pi.files.modelsHint': '由上方供应商管理自动合并生成;调整供应商请优先使用上方的供应商管理。',
1366
1366
  'pi.history.apply': '应用此记录',
1367
1367
  'pi.history.applied': '已应用历史记录',
1368
1368
  'pi.history.applyFailed': '应用历史记录失败',
@@ -49,19 +49,24 @@
49
49
  </div>
50
50
  <div class="card-trailing">
51
51
  <span v-if="piActiveProvider === providerId" class="pill configured">{{ t('pi.providers.active') }}</span>
52
- <button v-else type="button" class="btn-tool" @click.stop="switchPiActiveProvider(providerId)" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.setActive') }}</button>
52
+ <button v-else type="button" class="btn-tool btn-tool-compact" @click.stop="switchPiActiveProvider(providerId)" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.setActive') }}</button>
53
53
  </div>
54
54
  </div>
55
55
  </div>
56
- <div v-if="!piProviderIds.length" class="config-template-hint">{{ t('pi.providers.empty') }}</div>
56
+ <div v-if="!piProviderIds.length" class="empty-state">
57
+ <div class="empty-state-title">{{ t('pi.providers.empty') }}</div>
58
+ </div>
57
59
  </section>
58
60
 
59
- <section class="selector-section">
61
+ <section class="selector-section pi-file-section">
60
62
  <div class="selector-header">
61
- <span class="selector-title">{{ t('pi.files.title') }}</span>
63
+ <span class="selector-title">{{ t('pi.files.settingsTitle') }}</span>
64
+ <div class="selector-actions">
65
+ <button type="button" class="btn-tool btn-tool-compact" @click="openPiConfigHistory('settings')" :disabled="piHistoryLoading">{{ t('common.history') }}</button>
66
+ <button type="button" class="btn-tool btn-tool-compact" @click="savePiSettingsJson" :disabled="!isToolConfigWriteAllowed('pi') || piSaving || piFileJsonSaving || !!piSettingsJsonError">{{ t('common.save') }}</button>
67
+ </div>
62
68
  </div>
63
- <div class="config-template-hint">{{ t('pi.files.desc') }}</div>
64
- <label class="form-label">{{ t('pi.files.settingsTitle') }}</label>
69
+ <div class="config-template-hint">{{ t('pi.providers.targetFile', { path: '~/.pi/agent/settings.json' }) }}</div>
65
70
  <textarea
66
71
  class="template-textarea config-json-editor"
67
72
  v-model="piSettingsJsonDraft"
@@ -71,10 +76,6 @@
71
76
  @input="piSettingsJsonInput"
72
77
  placeholder="{}"></textarea>
73
78
  <p v-if="piSettingsJsonError" class="config-template-hint error-text">{{ piSettingsJsonError }}</p>
74
- <div class="settings-tab-actions">
75
- <button type="button" class="btn-tool" @click="savePiSettingsJson" :disabled="!isToolConfigWriteAllowed('pi') || piSaving || piFileJsonSaving || !!piSettingsJsonError">{{ t('common.save') }}</button>
76
- <button type="button" class="btn-mini" @click="openPiConfigHistory('settings')" :disabled="piHistoryLoading">{{ t('common.history') }}</button>
77
- </div>
78
79
  <details v-if="piHistoryTarget === 'settings'" class="prompt-history-panel" open>
79
80
  <summary class="prompt-history-summary">
80
81
  <span>{{ t('common.history') }}</span>
@@ -96,7 +97,18 @@
96
97
  </div>
97
98
  </div>
98
99
  </details>
99
- <label class="form-label">{{ t('pi.files.modelsTitle') }}</label>
100
+ </section>
101
+
102
+ <section class="selector-section pi-file-section">
103
+ <div class="selector-header">
104
+ <span class="selector-title">{{ t('pi.files.modelsTitle') }}</span>
105
+ <div class="selector-actions">
106
+ <button type="button" class="btn-tool btn-tool-compact" @click="openPiConfigHistory('models')" :disabled="piHistoryLoading">{{ t('common.history') }}</button>
107
+ <button type="button" class="btn-tool btn-tool-compact" @click="savePiModelsJson" :disabled="!isToolConfigWriteAllowed('pi') || piSaving || piFileJsonSaving || !!piModelsJsonError">{{ t('common.save') }}</button>
108
+ </div>
109
+ </div>
110
+ <div class="config-template-hint">{{ t('pi.files.modelsHint') }}</div>
111
+ <div class="config-template-hint">{{ t('pi.providers.targetFile', { path: '~/.pi/agent/models.json' }) }}</div>
100
112
  <textarea
101
113
  class="template-textarea config-json-editor"
102
114
  v-model="piModelsJsonDraft"
@@ -106,10 +118,6 @@
106
118
  @input="piModelsJsonInput"
107
119
  placeholder="{}"></textarea>
108
120
  <p v-if="piModelsJsonError" class="config-template-hint error-text">{{ piModelsJsonError }}</p>
109
- <div class="settings-tab-actions">
110
- <button type="button" class="btn-tool" @click="savePiModelsJson" :disabled="!isToolConfigWriteAllowed('pi') || piSaving || piFileJsonSaving || !!piModelsJsonError">{{ t('common.save') }}</button>
111
- <button type="button" class="btn-mini" @click="openPiConfigHistory('models')" :disabled="piHistoryLoading">{{ t('common.history') }}</button>
112
- </div>
113
121
  <details v-if="piHistoryTarget === 'models'" class="prompt-history-panel" open>
114
122
  <summary class="prompt-history-summary">
115
123
  <span>{{ t('common.history') }}</span>
@@ -3268,7 +3268,7 @@ return function render(_ctx, _cache) {
3268
3268
  : (_openBlock(), _createElementBlock("button", {
3269
3269
  key: 1,
3270
3270
  type: "button",
3271
- class: "btn-tool",
3271
+ class: "btn-tool btn-tool-compact",
3272
3272
  onClick: _withModifiers($event => (_ctx.switchPiActiveProvider(providerId)), ["stop"]),
3273
3273
  disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving
3274
3274
  }, _toDisplayString(_ctx.t('pi.providers.setActive')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]))
@@ -3280,16 +3280,31 @@ return function render(_ctx, _cache) {
3280
3280
  (!_ctx.piProviderIds.length)
3281
3281
  ? (_openBlock(), _createElementBlock("div", {
3282
3282
  key: 1,
3283
- class: "config-template-hint"
3284
- }, _toDisplayString(_ctx.t('pi.providers.empty')), 1 /* TEXT */))
3283
+ class: "empty-state"
3284
+ }, [
3285
+ _createElementVNode("div", { class: "empty-state-title" }, _toDisplayString(_ctx.t('pi.providers.empty')), 1 /* TEXT */)
3286
+ ]))
3285
3287
  : _createCommentVNode("v-if", true)
3286
3288
  ]),
3287
- _createElementVNode("section", { class: "selector-section" }, [
3289
+ _createElementVNode("section", { class: "selector-section pi-file-section" }, [
3288
3290
  _createElementVNode("div", { class: "selector-header" }, [
3289
- _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('pi.files.title')), 1 /* TEXT */)
3291
+ _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('pi.files.settingsTitle')), 1 /* TEXT */),
3292
+ _createElementVNode("div", { class: "selector-actions" }, [
3293
+ _createElementVNode("button", {
3294
+ type: "button",
3295
+ class: "btn-tool btn-tool-compact",
3296
+ onClick: $event => (_ctx.openPiConfigHistory('settings')),
3297
+ disabled: _ctx.piHistoryLoading
3298
+ }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3299
+ _createElementVNode("button", {
3300
+ type: "button",
3301
+ class: "btn-tool btn-tool-compact",
3302
+ onClick: _ctx.savePiSettingsJson,
3303
+ disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piSettingsJsonError
3304
+ }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3305
+ ])
3290
3306
  ]),
3291
- _createElementVNode("div", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.files.desc')), 1 /* TEXT */),
3292
- _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t('pi.files.settingsTitle')), 1 /* TEXT */),
3307
+ _createElementVNode("div", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.providers.targetFile', { path: '~/.pi/agent/settings.json' })), 1 /* TEXT */),
3293
3308
  _withDirectives(_createElementVNode("textarea", {
3294
3309
  class: "template-textarea config-json-editor",
3295
3310
  "onUpdate:modelValue": $event => ((_ctx.piSettingsJsonDraft) = $event),
@@ -3307,20 +3322,6 @@ return function render(_ctx, _cache) {
3307
3322
  class: "config-template-hint error-text"
3308
3323
  }, _toDisplayString(_ctx.piSettingsJsonError), 1 /* TEXT */))
3309
3324
  : _createCommentVNode("v-if", true),
3310
- _createElementVNode("div", { class: "settings-tab-actions" }, [
3311
- _createElementVNode("button", {
3312
- type: "button",
3313
- class: "btn-tool",
3314
- onClick: _ctx.savePiSettingsJson,
3315
- disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piSettingsJsonError
3316
- }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3317
- _createElementVNode("button", {
3318
- type: "button",
3319
- class: "btn-mini",
3320
- onClick: $event => (_ctx.openPiConfigHistory('settings')),
3321
- disabled: _ctx.piHistoryLoading
3322
- }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3323
- ]),
3324
3325
  (_ctx.piHistoryTarget === 'settings')
3325
3326
  ? (_openBlock(), _createElementBlock("details", {
3326
3327
  key: 1,
@@ -3386,8 +3387,28 @@ return function render(_ctx, _cache) {
3386
3387
  : _createCommentVNode("v-if", true)
3387
3388
  ])
3388
3389
  ]))
3389
- : _createCommentVNode("v-if", true),
3390
- _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t('pi.files.modelsTitle')), 1 /* TEXT */),
3390
+ : _createCommentVNode("v-if", true)
3391
+ ]),
3392
+ _createElementVNode("section", { class: "selector-section pi-file-section" }, [
3393
+ _createElementVNode("div", { class: "selector-header" }, [
3394
+ _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('pi.files.modelsTitle')), 1 /* TEXT */),
3395
+ _createElementVNode("div", { class: "selector-actions" }, [
3396
+ _createElementVNode("button", {
3397
+ type: "button",
3398
+ class: "btn-tool btn-tool-compact",
3399
+ onClick: $event => (_ctx.openPiConfigHistory('models')),
3400
+ disabled: _ctx.piHistoryLoading
3401
+ }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3402
+ _createElementVNode("button", {
3403
+ type: "button",
3404
+ class: "btn-tool btn-tool-compact",
3405
+ onClick: _ctx.savePiModelsJson,
3406
+ disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piModelsJsonError
3407
+ }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3408
+ ])
3409
+ ]),
3410
+ _createElementVNode("div", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.files.modelsHint')), 1 /* TEXT */),
3411
+ _createElementVNode("div", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.providers.targetFile', { path: '~/.pi/agent/models.json' })), 1 /* TEXT */),
3391
3412
  _withDirectives(_createElementVNode("textarea", {
3392
3413
  class: "template-textarea config-json-editor",
3393
3414
  "onUpdate:modelValue": $event => ((_ctx.piModelsJsonDraft) = $event),
@@ -3401,27 +3422,13 @@ return function render(_ctx, _cache) {
3401
3422
  ]),
3402
3423
  (_ctx.piModelsJsonError)
3403
3424
  ? (_openBlock(), _createElementBlock("p", {
3404
- key: 2,
3425
+ key: 0,
3405
3426
  class: "config-template-hint error-text"
3406
3427
  }, _toDisplayString(_ctx.piModelsJsonError), 1 /* TEXT */))
3407
3428
  : _createCommentVNode("v-if", true),
3408
- _createElementVNode("div", { class: "settings-tab-actions" }, [
3409
- _createElementVNode("button", {
3410
- type: "button",
3411
- class: "btn-tool",
3412
- onClick: _ctx.savePiModelsJson,
3413
- disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piModelsJsonError
3414
- }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3415
- _createElementVNode("button", {
3416
- type: "button",
3417
- class: "btn-mini",
3418
- onClick: $event => (_ctx.openPiConfigHistory('models')),
3419
- disabled: _ctx.piHistoryLoading
3420
- }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3421
- ]),
3422
3429
  (_ctx.piHistoryTarget === 'models')
3423
3430
  ? (_openBlock(), _createElementBlock("details", {
3424
- key: 3,
3431
+ key: 1,
3425
3432
  class: "prompt-history-panel",
3426
3433
  open: ""
3427
3434
  }, [
@@ -530,7 +530,7 @@ export async function loadActiveSessionDetail(api, options = {}) {
530
530
  }
531
531
  }
532
532
 
533
- export async function loadMoreSessionMessages() {
533
+ export async function loadMoreSessionMessages(stepSize) {
534
534
  if (this.mainTab !== 'sessions' || !this.sessionPreviewRenderEnabled) {
535
535
  return;
536
536
  }
@@ -541,6 +541,19 @@ export async function loadMoreSessionMessages() {
541
541
  this.sessionPreviewVisibleCount = 0;
542
542
  return;
543
543
  }
544
+ const step = Number.isFinite(stepSize)
545
+ ? Math.max(1, Math.floor(stepSize))
546
+ : (Number.isFinite(this.sessionPreviewLoadStep)
547
+ ? Math.max(1, Math.floor(this.sessionPreviewLoadStep))
548
+ : 40);
549
+ const current = Number.isFinite(this.sessionPreviewVisibleCount)
550
+ ? Math.max(0, Math.floor(this.sessionPreviewVisibleCount))
551
+ : 0;
552
+ const targetVisible = current + step;
553
+ if (targetVisible <= total) {
554
+ this.sessionPreviewVisibleCount = Math.min(total, targetVisible);
555
+ return;
556
+ }
544
557
 
545
558
  this.sessionPreviewVisibleCount = total;
546
559
  if (this.sessionDetailLoading) {
@@ -560,19 +573,24 @@ export async function loadMoreSessionMessages() {
560
573
  const currentLimit = Number.isFinite(currentLimitRaw)
561
574
  ? Math.max(1, Math.floor(currentLimitRaw))
562
575
  : Math.max(1, total);
576
+ const fetchStep = Number.isFinite(this.sessionDetailFetchStep)
577
+ ? Math.max(1, Math.floor(this.sessionDetailFetchStep))
578
+ : 80;
563
579
  const limitCapRaw = Number(this.sessionDetailMessageLimitCap);
564
580
  const limitCap = Number.isFinite(limitCapRaw)
565
581
  ? Math.max(1, Math.floor(limitCapRaw))
566
582
  : 1000;
567
583
 
568
- const nextLimit = totalKnown > 0
569
- ? Math.min(totalKnown, limitCap)
570
- : limitCap;
584
+ let nextLimit = Math.max(currentLimit + fetchStep, targetVisible);
585
+ if (totalKnown > total) {
586
+ nextLimit = Math.min(nextLimit, totalKnown);
587
+ }
588
+ nextLimit = Math.min(nextLimit, limitCap);
571
589
  if (nextLimit <= currentLimit) {
572
590
  return;
573
591
  }
574
592
 
575
- this.sessionPreviewPendingVisibleCount = nextLimit;
593
+ this.sessionPreviewPendingVisibleCount = targetVisible;
576
594
  this.sessionDetailMessageLimit = nextLimit;
577
595
  this.sessionPreviewLoadingMore = true;
578
596
  try {
@@ -580,11 +598,4 @@ export async function loadMoreSessionMessages() {
580
598
  } finally {
581
599
  this.sessionPreviewLoadingMore = false;
582
600
  }
583
-
584
- const loadedTotal = Array.isArray(this.activeSessionMessages)
585
- ? this.activeSessionMessages.length
586
- : 0;
587
- if (loadedTotal > this.sessionPreviewVisibleCount) {
588
- this.sessionPreviewVisibleCount = loadedTotal;
589
- }
590
601
  }
@@ -104,6 +104,13 @@
104
104
  flex: 0 0 auto;
105
105
  }
106
106
 
107
+ .selector-header .selector-actions > .btn-tool + .btn-tool,
108
+ .selector-header .selector-actions > .btn-tool-compact + .btn-tool,
109
+ .selector-header .selector-actions > .btn-tool + .btn-tool-compact,
110
+ .selector-header .selector-actions > .btn-tool-compact + .btn-tool-compact {
111
+ margin-top: 0;
112
+ }
113
+
107
114
  .selector-actions.opencode-provider-actions {
108
115
  display: inline-flex;
109
116
  align-items: stretch;