codexmate 0.1.18 → 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.18",
3
+ "version": "0.1.20",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -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;
@@ -268,6 +268,7 @@ const en = Object.freeze({
268
268
  'dashboard.doctor.export.json': 'Export JSON',
269
269
  'dashboard.doctor.export.md': 'Export Markdown',
270
270
  'dashboard.doctor.open': 'Open',
271
+ 'dashboard.doctor.empty': 'No checks have been run yet. Run checks to diagnose the current setup.',
271
272
  'doctor.action.openConfig': 'Open Config',
272
273
  'doctor.action.checkProvider': 'Check provider config',
273
274
  'doctor.action.openUsage': 'Open Usage',
@@ -1361,9 +1362,9 @@ const en = Object.freeze({
1361
1362
  'pi.providers.json.previewTitle': 'Preview',
1362
1363
  'pi.providers.json.editTitle': 'Edit JSON',
1363
1364
  'pi.files.title': 'File JSON',
1364
- '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.',
1365
1365
  'pi.files.settingsTitle': 'settings.json (active settings)',
1366
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.',
1367
1368
  'pi.history.apply': 'Apply this record',
1368
1369
  'pi.history.applied': 'History record applied',
1369
1370
  'pi.history.applyFailed': 'Failed to apply history record',
@@ -270,6 +270,7 @@ const ja = Object.freeze({
270
270
  'dashboard.doctor.export.json': 'JSON エクスポート',
271
271
  'dashboard.doctor.export.md': 'Markdown エクスポート',
272
272
  'dashboard.doctor.open': '開く',
273
+ 'dashboard.doctor.empty': 'まだチェックは実行されていません。「チェックを実行」で現在のセットアップを診断します。',
273
274
  'doctor.action.openConfig': 'Config を開く',
274
275
  'doctor.action.checkProvider': 'Provider 設定をチェック',
275
276
  'doctor.action.openUsage': 'Usage を開く',
@@ -1362,9 +1363,9 @@ const ja = Object.freeze({
1362
1363
  'pi.providers.json.previewTitle': 'プレビュー',
1363
1364
  'pi.providers.json.editTitle': 'JSON を編集',
1364
1365
  'pi.files.title': 'ファイル JSON',
1365
- 'pi.files.desc': 'Pi の settings.json と models.json を全文表示して直接編集します。保存前に JSON の正当性を検証し、不正な内容は書き込みません。',
1366
1366
  'pi.files.settingsTitle': 'settings.json(有効な設定)',
1367
1367
  'pi.files.modelsTitle': 'models.json(プロバイダーとモデル)',
1368
+ 'pi.files.modelsHint': '上のプロバイダー管理から自動的にマージされます。プロバイダーの変更は上の管理画面を優先してください。',
1368
1369
  'pi.history.apply': 'この履歴を適用',
1369
1370
  'pi.history.applied': '履歴を適用しました',
1370
1371
  'pi.history.applyFailed': '履歴の適用に失敗しました',
@@ -539,6 +539,7 @@ const vi = Object.freeze({
539
539
  'dashboard.doctor.export.json': 'Xuất JSON',
540
540
  'dashboard.doctor.export.md': 'Xuất Markdown',
541
541
  'dashboard.doctor.open': 'Mở',
542
+ 'dashboard.doctor.empty': 'Chưa chạy lần kiểm tra nào. Nhấn "Chạy kiểm tra" để chẩn đoán cấu hình hiện tại.',
542
543
  'doctor.action.openConfig': 'Mở cấu hình',
543
544
  'doctor.action.checkProvider': 'Kiểm tra cấu hình nhà cung cấp',
544
545
  'doctor.action.openUsage': 'Mở thống kê',
@@ -1330,9 +1331,9 @@ const vi = Object.freeze({
1330
1331
  'pi.providers.json.previewTitle': 'Xem trước',
1331
1332
  'pi.providers.json.editTitle': 'Chỉnh sửa JSON',
1332
1333
  'pi.files.title': 'JSON tập tin',
1333
- '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.',
1334
1334
  'pi.files.settingsTitle': 'settings.json (cài đặt đang dùng)',
1335
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 ở đó.',
1336
1337
  'pi.history.apply': 'Áp dụng bản ghi này',
1337
1338
  'pi.history.applied': 'Đã áp dụng lịch sử',
1338
1339
  'pi.history.applyFailed': 'Áp dụng lịch sử thất bại',
@@ -268,6 +268,7 @@ const zhTw = Object.freeze({
268
268
  'dashboard.doctor.export.json': '匯出 JSON',
269
269
  'dashboard.doctor.export.md': '匯出 Markdown',
270
270
  'dashboard.doctor.open': '打開',
271
+ 'dashboard.doctor.empty': '尚未執行檢查。點擊「運行檢查」診斷目前設定。',
271
272
  'doctor.action.openConfig': '打開 Config',
272
273
  'doctor.action.checkProvider': '檢查 Provider 設定',
273
274
  'doctor.action.openUsage': '打開 Usage',
@@ -1359,9 +1360,9 @@ const zhTw = Object.freeze({
1359
1360
  'pi.providers.json.previewTitle': '預覽',
1360
1361
  'pi.providers.json.editTitle': '編輯 JSON',
1361
1362
  'pi.files.title': '檔案 JSON',
1362
- 'pi.files.desc': '完整展示並直接編輯 Pi 的 settings.json 與 models.json;儲存前校驗 JSON 合法性,非法內容不會寫入。',
1363
1363
  'pi.files.settingsTitle': 'settings.json(生效設定)',
1364
1364
  'pi.files.modelsTitle': 'models.json(供應商與模型)',
1365
+ 'pi.files.modelsHint': '由上方供應商管理自動合併生成;調整供應商請優先使用上方的供應商管理。',
1365
1366
  'pi.history.apply': '套用此紀錄',
1366
1367
  'pi.history.applied': '已套用歷史紀錄',
1367
1368
  'pi.history.applyFailed': '套用歷史紀錄失敗',
@@ -268,6 +268,7 @@ const zh = Object.freeze({
268
268
  'dashboard.doctor.export.json': '导出 JSON',
269
269
  'dashboard.doctor.export.md': '导出 Markdown',
270
270
  'dashboard.doctor.open': '打开',
271
+ 'dashboard.doctor.empty': '尚未运行检查。点击上方“运行检查”诊断当前配置。',
271
272
  'doctor.action.openConfig': '打开 Config',
272
273
  'doctor.action.checkProvider': '检查 Provider 配置',
273
274
  'doctor.action.openUsage': '打开 Usage',
@@ -1359,9 +1360,9 @@ const zh = Object.freeze({
1359
1360
  'pi.providers.json.previewTitle': '预览',
1360
1361
  'pi.providers.json.editTitle': '编辑 JSON',
1361
1362
  'pi.files.title': '文件 JSON',
1362
- 'pi.files.desc': '完整展示并直接编辑 Pi 的 settings.json 与 models.json;保存前校验 JSON 合法性,非法内容不会写入。',
1363
1363
  'pi.files.settingsTitle': 'settings.json(生效设置)',
1364
1364
  'pi.files.modelsTitle': 'models.json(供应商与模型)',
1365
+ 'pi.files.modelsHint': '由上方供应商管理自动合并生成;调整供应商请优先使用上方的供应商管理。',
1365
1366
  'pi.history.apply': '应用此记录',
1366
1367
  'pi.history.applied': '已应用历史记录',
1367
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>
@@ -5,11 +5,11 @@
5
5
  role="tabpanel"
6
6
  :aria-labelledby="'tab-dashboard'">
7
7
  <div class="selector-section doctor-hero">
8
- <div class="selector-header">
8
+ <div class="selector-header doctor-header">
9
9
  <span class="selector-title">{{ t('dashboard.doctor.title') }}</span>
10
10
  <div class="selector-actions">
11
11
  <button
12
- class="btn-tool btn-tool-compact"
12
+ class="btn-tool btn-tool-compact doctor-run-btn"
13
13
  @click="runHealthCheck"
14
14
  :disabled="loading || !!initError || healthCheckLoading">
15
15
  {{ healthCheckLoading ? t('dashboard.doctor.checking') : t('dashboard.doctor.runChecks') }}
@@ -30,52 +30,59 @@
30
30
  <strong>{{ inspectorModelLoadStatus }}</strong>
31
31
  </div>
32
32
  </div>
33
- <div v-if="healthCheckResult" class="doctor-health-result" :class="healthCheckResult.ok ? 'ok' : 'error'">
34
- <div class="doctor-health-title">
35
- {{ healthCheckResult.ok ? t('dashboard.health.ok') : t('dashboard.health.fail') }}
36
- <span v-if="healthCheckResult.issues && healthCheckResult.issues.length">({{ t('dashboard.health.issues', { count: healthCheckResult.issues.length }) }})</span>
37
- </div>
38
- </div>
39
- <div v-if="healthCheckResult && healthCheckResult.report" class="doctor-action-list">
40
- <template v-if="healthCheckResult.report.issues && healthCheckResult.report.issues.length">
41
- <div v-for="issue in healthCheckResult.report.issues" :key="issue.id" class="doctor-action-card">
42
- <div class="doctor-action-head">
43
- <div class="doctor-action-title">{{ issue.problem || (issue.problemKey ? t(issue.problemKey, issue.problemParams) : '') }}</div>
44
- <div :class="['doctor-action-severity', issue.severity]">{{ issue.severityLabel || issue.severity }}</div>
33
+ <div class="doctor-result-section">
34
+ <template v-if="healthCheckResult">
35
+ <div class="doctor-health-result" :class="healthCheckResult.ok ? 'ok' : 'error'">
36
+ <div class="doctor-health-title">
37
+ {{ healthCheckResult.ok ? t('dashboard.health.ok') : t('dashboard.health.fail') }}
38
+ <span v-if="healthCheckResult.issues && healthCheckResult.issues.length">({{ t('dashboard.health.issues', { count: healthCheckResult.issues.length }) }})</span>
45
39
  </div>
46
- <div class="doctor-action-impact">{{ issue.impact || (issue.impactKey ? t(issue.impactKey, issue.impactParams) : '') }}</div>
47
- <div
48
- v-if="issue.actions && issue.actions.some(action => action && action.type === 'navigate' && action.target)"
49
- class="doctor-action-buttons">
50
- <template v-for="(action, index) in issue.actions" :key="issue.id + '-action-' + index">
51
- <button
52
- v-if="action.type === 'navigate' && action.target"
53
- type="button"
54
- class="btn-tool btn-tool-compact"
55
- @click="action.target ? switchMainTab(action.target) : null">
56
- {{ action.label || (action.labelKey ? t(action.labelKey, action.labelParams) : t('dashboard.doctor.open')) }}
57
- </button>
58
- </template>
40
+ </div>
41
+ <div v-if="healthCheckResult.report" class="doctor-action-list">
42
+ <template v-if="healthCheckResult.report.issues && healthCheckResult.report.issues.length">
43
+ <div v-for="issue in healthCheckResult.report.issues" :key="issue.id" class="doctor-action-card">
44
+ <div class="doctor-action-head">
45
+ <div class="doctor-action-title">{{ issue.problem || (issue.problemKey ? t(issue.problemKey, issue.problemParams) : '') }}</div>
46
+ <div :class="['doctor-action-severity', issue.severity]">{{ issue.severityLabel || issue.severity }}</div>
47
+ </div>
48
+ <div class="doctor-action-impact">{{ issue.impact || (issue.impactKey ? t(issue.impactKey, issue.impactParams) : '') }}</div>
49
+ <div
50
+ v-if="issue.actions && issue.actions.some(action => action && action.type === 'navigate' && action.target)"
51
+ class="doctor-action-buttons">
52
+ <template v-for="(action, index) in issue.actions" :key="issue.id + '-action-' + index">
53
+ <button
54
+ v-if="action.type === 'navigate' && action.target"
55
+ type="button"
56
+ class="btn-tool btn-tool-compact"
57
+ @click="action.target ? switchMainTab(action.target) : null">
58
+ {{ action.label || (action.labelKey ? t(action.labelKey, action.labelParams) : t('dashboard.doctor.open')) }}
59
+ </button>
60
+ </template>
61
+ </div>
62
+ </div>
63
+ </template>
64
+ <div class="doctor-action-footer">
65
+ <button
66
+ type="button"
67
+ class="btn-tool btn-tool-compact"
68
+ @click="healthCheckResult && healthCheckResult.report
69
+ ? downloadTextFile('codexmate-doctor.json', JSON.stringify(healthCheckResult.report, null, 2), 'application/json;charset=utf-8')
70
+ : null">
71
+ {{ t('dashboard.doctor.export.json') }}
72
+ </button>
73
+ <button
74
+ type="button"
75
+ class="btn-tool btn-tool-compact"
76
+ @click="healthCheckResult && healthCheckResult.report
77
+ ? downloadTextFile('codexmate-doctor.md', String(healthCheckResult.markdown || ''), 'text/markdown;charset=utf-8')
78
+ : null">
79
+ {{ t('dashboard.doctor.export.md') }}
80
+ </button>
59
81
  </div>
60
82
  </div>
61
83
  </template>
62
- <div class="doctor-action-footer">
63
- <button
64
- type="button"
65
- class="btn-tool btn-tool-compact"
66
- @click="healthCheckResult && healthCheckResult.report
67
- ? downloadTextFile('codexmate-doctor.json', JSON.stringify(healthCheckResult.report, null, 2), 'application/json;charset=utf-8')
68
- : null">
69
- {{ t('dashboard.doctor.export.json') }}
70
- </button>
71
- <button
72
- type="button"
73
- class="btn-tool btn-tool-compact"
74
- @click="healthCheckResult && healthCheckResult.report
75
- ? downloadTextFile('codexmate-doctor.md', String(healthCheckResult.markdown || ''), 'text/markdown;charset=utf-8')
76
- : null">
77
- {{ t('dashboard.doctor.export.md') }}
78
- </button>
84
+ <div v-else class="doctor-result-empty">
85
+ <span class="doctor-result-empty-text">{{ t('dashboard.doctor.empty') }}</span>
79
86
  </div>
80
87
  </div>
81
88
  <div class="doctor-grid">
@@ -819,11 +819,11 @@ return function render(_ctx, _cache) {
819
819
  "aria-labelledby": 'tab-dashboard'
820
820
  }, [
821
821
  _createElementVNode("div", { class: "selector-section doctor-hero" }, [
822
- _createElementVNode("div", { class: "selector-header" }, [
822
+ _createElementVNode("div", { class: "selector-header doctor-header" }, [
823
823
  _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('dashboard.doctor.title')), 1 /* TEXT */),
824
824
  _createElementVNode("div", { class: "selector-actions" }, [
825
825
  _createElementVNode("button", {
826
- class: "btn-tool btn-tool-compact",
826
+ class: "btn-tool btn-tool-compact doctor-run-btn",
827
827
  onClick: _ctx.runHealthCheck,
828
828
  disabled: _ctx.loading || !!_ctx.initError || _ctx.healthCheckLoading
829
829
  }, _toDisplayString(_ctx.healthCheckLoading ? _ctx.t('dashboard.doctor.checking') : _ctx.t('dashboard.doctor.runChecks')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
@@ -845,79 +845,87 @@ return function render(_ctx, _cache) {
845
845
  _createElementVNode("strong", null, _toDisplayString(_ctx.inspectorModelLoadStatus), 1 /* TEXT */)
846
846
  ])
847
847
  ]),
848
- (_ctx.healthCheckResult)
849
- ? (_openBlock(), _createElementBlock("div", {
850
- key: 0,
851
- class: _normalizeClass(["doctor-health-result", _ctx.healthCheckResult.ok ? 'ok' : 'error'])
852
- }, [
853
- _createElementVNode("div", { class: "doctor-health-title" }, [
854
- _createTextVNode(_toDisplayString(_ctx.healthCheckResult.ok ? _ctx.t('dashboard.health.ok') : _ctx.t('dashboard.health.fail')) + " ", 1 /* TEXT */),
855
- (_ctx.healthCheckResult.issues && _ctx.healthCheckResult.issues.length)
856
- ? (_openBlock(), _createElementBlock("span", { key: 0 }, "(" + _toDisplayString(_ctx.t('dashboard.health.issues', { count: _ctx.healthCheckResult.issues.length })) + ")", 1 /* TEXT */))
857
- : _createCommentVNode("v-if", true)
858
- ])
859
- ], 2 /* CLASS */))
860
- : _createCommentVNode("v-if", true),
861
- (_ctx.healthCheckResult && _ctx.healthCheckResult.report)
862
- ? (_openBlock(), _createElementBlock("div", {
863
- key: 1,
864
- class: "doctor-action-list"
865
- }, [
866
- (_ctx.healthCheckResult.report.issues && _ctx.healthCheckResult.report.issues.length)
867
- ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(_ctx.healthCheckResult.report.issues, (issue) => {
868
- return (_openBlock(), _createElementBlock("div", {
869
- key: issue.id,
870
- class: "doctor-action-card"
848
+ _createElementVNode("div", { class: "doctor-result-section" }, [
849
+ (_ctx.healthCheckResult)
850
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
851
+ _createElementVNode("div", {
852
+ class: _normalizeClass(["doctor-health-result", _ctx.healthCheckResult.ok ? 'ok' : 'error'])
853
+ }, [
854
+ _createElementVNode("div", { class: "doctor-health-title" }, [
855
+ _createTextVNode(_toDisplayString(_ctx.healthCheckResult.ok ? _ctx.t('dashboard.health.ok') : _ctx.t('dashboard.health.fail')) + " ", 1 /* TEXT */),
856
+ (_ctx.healthCheckResult.issues && _ctx.healthCheckResult.issues.length)
857
+ ? (_openBlock(), _createElementBlock("span", { key: 0 }, "(" + _toDisplayString(_ctx.t('dashboard.health.issues', { count: _ctx.healthCheckResult.issues.length })) + ")", 1 /* TEXT */))
858
+ : _createCommentVNode("v-if", true)
859
+ ])
860
+ ], 2 /* CLASS */),
861
+ (_ctx.healthCheckResult.report)
862
+ ? (_openBlock(), _createElementBlock("div", {
863
+ key: 0,
864
+ class: "doctor-action-list"
871
865
  }, [
872
- _createElementVNode("div", { class: "doctor-action-head" }, [
873
- _createElementVNode("div", { class: "doctor-action-title" }, _toDisplayString(issue.problem || (issue.problemKey ? _ctx.t(issue.problemKey, issue.problemParams) : '')), 1 /* TEXT */),
874
- _createElementVNode("div", {
875
- class: _normalizeClass(['doctor-action-severity', issue.severity])
876
- }, _toDisplayString(issue.severityLabel || issue.severity), 3 /* TEXT, CLASS */)
877
- ]),
878
- _createElementVNode("div", { class: "doctor-action-impact" }, _toDisplayString(issue.impact || (issue.impactKey ? _ctx.t(issue.impactKey, issue.impactParams) : '')), 1 /* TEXT */),
879
- (issue.actions && issue.actions.some(action => action && action.type === 'navigate' && action.target))
880
- ? (_openBlock(), _createElementBlock("div", {
881
- key: 0,
882
- class: "doctor-action-buttons"
883
- }, [
884
- (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(issue.actions, (action, index) => {
885
- return (_openBlock(), _createElementBlock(_Fragment, {
886
- key: issue.id + '-action-' + index
887
- }, [
888
- (action.type === 'navigate' && action.target)
889
- ? (_openBlock(), _createElementBlock("button", {
890
- key: 0,
891
- type: "button",
892
- class: "btn-tool btn-tool-compact",
893
- onClick: $event => (action.target ? _ctx.switchMainTab(action.target) : null)
894
- }, _toDisplayString(action.label || (action.labelKey ? _ctx.t(action.labelKey, action.labelParams) : _ctx.t('dashboard.doctor.open'))), 9 /* TEXT, PROPS */, ["onClick"]))
895
- : _createCommentVNode("v-if", true)
896
- ], 64 /* STABLE_FRAGMENT */))
897
- }), 128 /* KEYED_FRAGMENT */))
898
- ]))
899
- : _createCommentVNode("v-if", true)
866
+ (_ctx.healthCheckResult.report.issues && _ctx.healthCheckResult.report.issues.length)
867
+ ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(_ctx.healthCheckResult.report.issues, (issue) => {
868
+ return (_openBlock(), _createElementBlock("div", {
869
+ key: issue.id,
870
+ class: "doctor-action-card"
871
+ }, [
872
+ _createElementVNode("div", { class: "doctor-action-head" }, [
873
+ _createElementVNode("div", { class: "doctor-action-title" }, _toDisplayString(issue.problem || (issue.problemKey ? _ctx.t(issue.problemKey, issue.problemParams) : '')), 1 /* TEXT */),
874
+ _createElementVNode("div", {
875
+ class: _normalizeClass(['doctor-action-severity', issue.severity])
876
+ }, _toDisplayString(issue.severityLabel || issue.severity), 3 /* TEXT, CLASS */)
877
+ ]),
878
+ _createElementVNode("div", { class: "doctor-action-impact" }, _toDisplayString(issue.impact || (issue.impactKey ? _ctx.t(issue.impactKey, issue.impactParams) : '')), 1 /* TEXT */),
879
+ (issue.actions && issue.actions.some(action => action && action.type === 'navigate' && action.target))
880
+ ? (_openBlock(), _createElementBlock("div", {
881
+ key: 0,
882
+ class: "doctor-action-buttons"
883
+ }, [
884
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(issue.actions, (action, index) => {
885
+ return (_openBlock(), _createElementBlock(_Fragment, {
886
+ key: issue.id + '-action-' + index
887
+ }, [
888
+ (action.type === 'navigate' && action.target)
889
+ ? (_openBlock(), _createElementBlock("button", {
890
+ key: 0,
891
+ type: "button",
892
+ class: "btn-tool btn-tool-compact",
893
+ onClick: $event => (action.target ? _ctx.switchMainTab(action.target) : null)
894
+ }, _toDisplayString(action.label || (action.labelKey ? _ctx.t(action.labelKey, action.labelParams) : _ctx.t('dashboard.doctor.open'))), 9 /* TEXT, PROPS */, ["onClick"]))
895
+ : _createCommentVNode("v-if", true)
896
+ ], 64 /* STABLE_FRAGMENT */))
897
+ }), 128 /* KEYED_FRAGMENT */))
898
+ ]))
899
+ : _createCommentVNode("v-if", true)
900
+ ]))
901
+ }), 128 /* KEYED_FRAGMENT */))
902
+ : _createCommentVNode("v-if", true),
903
+ _createElementVNode("div", { class: "doctor-action-footer" }, [
904
+ _createElementVNode("button", {
905
+ type: "button",
906
+ class: "btn-tool btn-tool-compact",
907
+ onClick: $event => {_ctx.healthCheckResult && _ctx.healthCheckResult.report
908
+ ? _ctx.downloadTextFile('codexmate-doctor.json', JSON.stringify(_ctx.healthCheckResult.report, null, 2), 'application/json;charset=utf-8')
909
+ : null}
910
+ }, _toDisplayString(_ctx.t('dashboard.doctor.export.json')), 9 /* TEXT, PROPS */, ["onClick"]),
911
+ _createElementVNode("button", {
912
+ type: "button",
913
+ class: "btn-tool btn-tool-compact",
914
+ onClick: $event => {_ctx.healthCheckResult && _ctx.healthCheckResult.report
915
+ ? _ctx.downloadTextFile('codexmate-doctor.md', String(_ctx.healthCheckResult.markdown || ''), 'text/markdown;charset=utf-8')
916
+ : null}
917
+ }, _toDisplayString(_ctx.t('dashboard.doctor.export.md')), 9 /* TEXT, PROPS */, ["onClick"])
918
+ ])
900
919
  ]))
901
- }), 128 /* KEYED_FRAGMENT */))
902
- : _createCommentVNode("v-if", true),
903
- _createElementVNode("div", { class: "doctor-action-footer" }, [
904
- _createElementVNode("button", {
905
- type: "button",
906
- class: "btn-tool btn-tool-compact",
907
- onClick: $event => {_ctx.healthCheckResult && _ctx.healthCheckResult.report
908
- ? _ctx.downloadTextFile('codexmate-doctor.json', JSON.stringify(_ctx.healthCheckResult.report, null, 2), 'application/json;charset=utf-8')
909
- : null}
910
- }, _toDisplayString(_ctx.t('dashboard.doctor.export.json')), 9 /* TEXT, PROPS */, ["onClick"]),
911
- _createElementVNode("button", {
912
- type: "button",
913
- class: "btn-tool btn-tool-compact",
914
- onClick: $event => {_ctx.healthCheckResult && _ctx.healthCheckResult.report
915
- ? _ctx.downloadTextFile('codexmate-doctor.md', String(_ctx.healthCheckResult.markdown || ''), 'text/markdown;charset=utf-8')
916
- : null}
917
- }, _toDisplayString(_ctx.t('dashboard.doctor.export.md')), 9 /* TEXT, PROPS */, ["onClick"])
918
- ])
919
- ]))
920
- : _createCommentVNode("v-if", true),
920
+ : _createCommentVNode("v-if", true)
921
+ ], 64 /* STABLE_FRAGMENT */))
922
+ : (_openBlock(), _createElementBlock("div", {
923
+ key: 1,
924
+ class: "doctor-result-empty"
925
+ }, [
926
+ _createElementVNode("span", { class: "doctor-result-empty-text" }, _toDisplayString(_ctx.t('dashboard.doctor.empty')), 1 /* TEXT */)
927
+ ]))
928
+ ]),
921
929
  _createElementVNode("div", { class: "doctor-grid" }, [
922
930
  _createElementVNode("button", {
923
931
  type: "button",
@@ -3260,7 +3268,7 @@ return function render(_ctx, _cache) {
3260
3268
  : (_openBlock(), _createElementBlock("button", {
3261
3269
  key: 1,
3262
3270
  type: "button",
3263
- class: "btn-tool",
3271
+ class: "btn-tool btn-tool-compact",
3264
3272
  onClick: _withModifiers($event => (_ctx.switchPiActiveProvider(providerId)), ["stop"]),
3265
3273
  disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving
3266
3274
  }, _toDisplayString(_ctx.t('pi.providers.setActive')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]))
@@ -3272,16 +3280,31 @@ return function render(_ctx, _cache) {
3272
3280
  (!_ctx.piProviderIds.length)
3273
3281
  ? (_openBlock(), _createElementBlock("div", {
3274
3282
  key: 1,
3275
- class: "config-template-hint"
3276
- }, _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
+ ]))
3277
3287
  : _createCommentVNode("v-if", true)
3278
3288
  ]),
3279
- _createElementVNode("section", { class: "selector-section" }, [
3289
+ _createElementVNode("section", { class: "selector-section pi-file-section" }, [
3280
3290
  _createElementVNode("div", { class: "selector-header" }, [
3281
- _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
+ ])
3282
3306
  ]),
3283
- _createElementVNode("div", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.files.desc')), 1 /* TEXT */),
3284
- _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 */),
3285
3308
  _withDirectives(_createElementVNode("textarea", {
3286
3309
  class: "template-textarea config-json-editor",
3287
3310
  "onUpdate:modelValue": $event => ((_ctx.piSettingsJsonDraft) = $event),
@@ -3299,20 +3322,6 @@ return function render(_ctx, _cache) {
3299
3322
  class: "config-template-hint error-text"
3300
3323
  }, _toDisplayString(_ctx.piSettingsJsonError), 1 /* TEXT */))
3301
3324
  : _createCommentVNode("v-if", true),
3302
- _createElementVNode("div", { class: "settings-tab-actions" }, [
3303
- _createElementVNode("button", {
3304
- type: "button",
3305
- class: "btn-tool",
3306
- onClick: _ctx.savePiSettingsJson,
3307
- disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piSettingsJsonError
3308
- }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3309
- _createElementVNode("button", {
3310
- type: "button",
3311
- class: "btn-mini",
3312
- onClick: $event => (_ctx.openPiConfigHistory('settings')),
3313
- disabled: _ctx.piHistoryLoading
3314
- }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3315
- ]),
3316
3325
  (_ctx.piHistoryTarget === 'settings')
3317
3326
  ? (_openBlock(), _createElementBlock("details", {
3318
3327
  key: 1,
@@ -3378,8 +3387,28 @@ return function render(_ctx, _cache) {
3378
3387
  : _createCommentVNode("v-if", true)
3379
3388
  ])
3380
3389
  ]))
3381
- : _createCommentVNode("v-if", true),
3382
- _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 */),
3383
3412
  _withDirectives(_createElementVNode("textarea", {
3384
3413
  class: "template-textarea config-json-editor",
3385
3414
  "onUpdate:modelValue": $event => ((_ctx.piModelsJsonDraft) = $event),
@@ -3393,27 +3422,13 @@ return function render(_ctx, _cache) {
3393
3422
  ]),
3394
3423
  (_ctx.piModelsJsonError)
3395
3424
  ? (_openBlock(), _createElementBlock("p", {
3396
- key: 2,
3425
+ key: 0,
3397
3426
  class: "config-template-hint error-text"
3398
3427
  }, _toDisplayString(_ctx.piModelsJsonError), 1 /* TEXT */))
3399
3428
  : _createCommentVNode("v-if", true),
3400
- _createElementVNode("div", { class: "settings-tab-actions" }, [
3401
- _createElementVNode("button", {
3402
- type: "button",
3403
- class: "btn-tool",
3404
- onClick: _ctx.savePiModelsJson,
3405
- disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving || _ctx.piFileJsonSaving || !!_ctx.piModelsJsonError
3406
- }, _toDisplayString(_ctx.t('common.save')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
3407
- _createElementVNode("button", {
3408
- type: "button",
3409
- class: "btn-mini",
3410
- onClick: $event => (_ctx.openPiConfigHistory('models')),
3411
- disabled: _ctx.piHistoryLoading
3412
- }, _toDisplayString(_ctx.t('common.history')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3413
- ]),
3414
3429
  (_ctx.piHistoryTarget === 'models')
3415
3430
  ? (_openBlock(), _createElementBlock("details", {
3416
- key: 3,
3431
+ key: 1,
3417
3432
  class: "prompt-history-panel",
3418
3433
  open: ""
3419
3434
  }, [
@@ -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;
@@ -4,9 +4,10 @@
4
4
 
5
5
  .doctor-grid {
6
6
  display: grid;
7
- grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
7
+ grid-template-columns: repeat(4, minmax(0, 1fr));
8
8
  gap: 12px;
9
9
  margin-top: 12px;
10
+ align-items: stretch;
10
11
  }
11
12
 
12
13
  .doctor-card {
@@ -102,14 +103,16 @@
102
103
  display: inline-flex;
103
104
  align-items: center;
104
105
  gap: 8px;
105
- padding: 8px 10px;
106
+ min-width: 150px;
107
+ padding: 8px 12px;
106
108
  border-radius: 999px;
107
109
  border: 1px solid var(--color-border-soft);
108
110
  background: rgba(255, 255, 255, 0.78);
109
111
  font-size: var(--font-size-caption);
110
112
  color: var(--color-text-secondary);
111
- justify-content: center;
112
- text-align: center;
113
+ justify-content: space-between;
114
+ text-align: left;
115
+ font-variant-numeric: tabular-nums;
113
116
  }
114
117
 
115
118
  .doctor-status-chip strong {
@@ -162,27 +165,6 @@
162
165
  text-align: left;
163
166
  }
164
167
 
165
- .doctor-health-issues {
166
- display: flex;
167
- flex-direction: column;
168
- gap: 6px;
169
- font-size: var(--font-size-caption);
170
- align-items: center;
171
- }
172
-
173
- .doctor-health-issue {
174
- padding: 8px 10px;
175
- border-radius: 12px;
176
- border: 1px solid rgba(220, 95, 108, 0.2);
177
- background: rgba(255, 255, 255, 0.75);
178
- color: var(--color-text-secondary);
179
- overflow-wrap: anywhere;
180
- word-break: break-word;
181
- text-align: center;
182
- width: 100%;
183
- max-width: 720px;
184
- }
185
-
186
168
  .doctor-action-list {
187
169
  margin-top: 14px;
188
170
  display: grid;
@@ -212,8 +194,11 @@
212
194
  color: var(--color-text-primary);
213
195
  min-width: 0;
214
196
  overflow: hidden;
215
- text-overflow: ellipsis;
216
- white-space: nowrap;
197
+ display: -webkit-box;
198
+ -webkit-box-orient: vertical;
199
+ -webkit-line-clamp: 2;
200
+ overflow-wrap: anywhere;
201
+ word-break: break-word;
217
202
  max-width: 100%;
218
203
  flex: 1 1 220px;
219
204
  }
@@ -271,6 +256,46 @@
271
256
  margin-top: 2px;
272
257
  }
273
258
 
259
+ .doctor-result-section {
260
+ margin-top: 14px;
261
+ display: flex;
262
+ flex-direction: column;
263
+ gap: 12px;
264
+ }
265
+
266
+ .doctor-result-section > .doctor-health-result,
267
+ .doctor-result-section > .doctor-action-list {
268
+ margin-top: 0;
269
+ }
270
+
271
+ .doctor-result-empty {
272
+ border: 1px dashed var(--color-border-soft);
273
+ border-radius: var(--radius-lg);
274
+ background: rgba(255, 255, 255, 0.5);
275
+ padding: 16px;
276
+ text-align: center;
277
+ font-size: var(--font-size-caption);
278
+ color: var(--color-text-tertiary);
279
+ }
280
+
281
+ .doctor-header .selector-actions > .doctor-run-btn {
282
+ background: linear-gradient(135deg, var(--color-brand) 0%, var(--color-brand-dark) 100%);
283
+ border-color: var(--color-brand-dark);
284
+ color: #fff;
285
+ }
286
+
287
+ .doctor-header .selector-actions > .doctor-run-btn:not(:disabled):hover {
288
+ border-color: var(--color-brand-dark);
289
+ color: #fff;
290
+ filter: brightness(1.05);
291
+ }
292
+
293
+ @media (max-width: 1280px) {
294
+ .doctor-grid {
295
+ grid-template-columns: repeat(2, minmax(0, 1fr));
296
+ }
297
+ }
298
+
274
299
  @media (max-width: 520px) {
275
300
  .doctor-grid {
276
301
  grid-template-columns: 1fr;
@@ -305,134 +330,3 @@
305
330
  }
306
331
  }
307
332
 
308
- .doctor-providers-health {
309
- margin-top: 12px;
310
- padding: 12px;
311
- border-radius: var(--radius-lg);
312
- border: 1px solid var(--color-border-soft);
313
- background: rgba(255, 255, 255, 0.78);
314
- }
315
-
316
- .doctor-providers-health-title {
317
- font-size: var(--font-size-body);
318
- font-weight: var(--font-weight-title);
319
- color: var(--color-text-primary);
320
- margin-bottom: 10px;
321
- display: flex;
322
- align-items: center;
323
- gap: 8px;
324
- }
325
-
326
- .doctor-providers-health-summary {
327
- font-size: var(--font-size-caption);
328
- font-weight: var(--font-weight-primary);
329
- color: var(--color-text-tertiary);
330
- }
331
-
332
- .doctor-providers-grid {
333
- display: flex;
334
- flex-wrap: wrap;
335
- gap: 8px;
336
- }
337
-
338
- .doctor-provider-chip {
339
- display: inline-flex;
340
- align-items: center;
341
- gap: 6px;
342
- padding: 6px 12px;
343
- border-radius: 999px;
344
- border: 1px solid var(--color-border-soft);
345
- background: rgba(255, 255, 255, 0.85);
346
- font-size: var(--font-size-caption);
347
- color: var(--color-text-secondary);
348
- max-width: 100%;
349
- overflow: hidden;
350
- }
351
-
352
- .doctor-provider-chip.green {
353
- border-color: rgba(57, 181, 97, 0.25);
354
- background: rgba(57, 181, 97, 0.08);
355
- color: #2b6a3b;
356
- }
357
-
358
- .doctor-provider-chip.yellow {
359
- border-color: rgba(246, 211, 106, 0.45);
360
- background: rgba(246, 211, 106, 0.16);
361
- color: #6f4b00;
362
- }
363
-
364
- .doctor-provider-chip.red {
365
- border-color: rgba(220, 95, 108, 0.28);
366
- background: rgba(220, 95, 108, 0.08);
367
- color: #8a2f36;
368
- }
369
-
370
- .doctor-provider-name {
371
- overflow: hidden;
372
- text-overflow: ellipsis;
373
- white-space: nowrap;
374
- }
375
-
376
- .doctor-provider-current {
377
- font-size: 0.85em;
378
- opacity: 0.7;
379
- margin-left: 2px;
380
- }
381
-
382
- .doctor-provider-status-dot {
383
- width: 8px;
384
- height: 8px;
385
- border-radius: 50%;
386
- flex-shrink: 0;
387
- }
388
-
389
- .doctor-provider-chip.green .doctor-provider-status-dot {
390
- background: rgba(57, 181, 97, 0.7);
391
- }
392
-
393
- .doctor-provider-chip.yellow .doctor-provider-status-dot {
394
- background: rgba(246, 211, 106, 0.85);
395
- }
396
-
397
- .doctor-provider-chip.red .doctor-provider-status-dot {
398
- background: rgba(220, 95, 108, 0.7);
399
- }
400
-
401
- .doctor-provider-issues {
402
- margin-top: 10px;
403
- display: flex;
404
- flex-direction: column;
405
- gap: 6px;
406
- }
407
-
408
- .doctor-provider-issue-card {
409
- padding: 8px 10px;
410
- border-radius: 10px;
411
- border: 1px solid rgba(220, 95, 108, 0.15);
412
- background: rgba(255, 255, 255, 0.7);
413
- }
414
-
415
- .doctor-provider-issue-name {
416
- font-weight: var(--font-weight-secondary);
417
- color: var(--color-text-primary);
418
- margin-bottom: 4px;
419
- }
420
-
421
- .doctor-provider-issue-row {
422
- font-size: var(--font-size-caption);
423
- color: var(--color-text-secondary);
424
- display: flex;
425
- flex-direction: column;
426
- gap: 2px;
427
- padding-left: 8px;
428
- margin-top: 4px;
429
- }
430
-
431
- .doctor-provider-issue-text {
432
- color: #8a2f36;
433
- }
434
-
435
- .doctor-provider-issue-fix {
436
- color: var(--color-text-tertiary);
437
- font-style: italic;
438
- }