codexmate 0.1.13 → 0.1.15

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.13",
3
+ "version": "0.1.15",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/web-ui/app.js CHANGED
@@ -587,7 +587,11 @@ document.addEventListener('DOMContentLoaded', () => {
587
587
  return true;
588
588
  }
589
589
  })();
590
- void this.loadWebUiPreferences({ applyNavigation: applyPreferenceNavigation });
590
+ this.loadWebUiPreferences({ applyNavigation: applyPreferenceNavigation }).then(() => {
591
+ if (this.mainTab === 'prompts' && typeof this.loadPromptsTabContent === 'function') {
592
+ this.loadPromptsTabContent();
593
+ }
594
+ });
591
595
  }
592
596
  if (typeof this.t === 'function') {
593
597
  this.confirmDialogConfirmText = this.t('confirm.ok');
@@ -786,14 +790,7 @@ document.addEventListener('DOMContentLoaded', () => {
786
790
  return;
787
791
  }
788
792
  if (newTab === 'prompts') {
789
- if (this.promptsSubTab === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading && typeof this.loadProjectPathOptions === 'function') {
790
- this.loadProjectPathOptions();
791
- }
792
- if (this.promptsSubTab === 'system') {
793
- if (typeof this.loadSystemPrompt === 'function') this.loadSystemPrompt();
794
- } else if (typeof this.loadPromptsContent === 'function') {
795
- this.loadPromptsContent();
796
- }
793
+ if (typeof this.loadPromptsTabContent === 'function') this.loadPromptsTabContent();
797
794
  }
798
795
  },
799
796
  promptsSubTab(newVal) {
@@ -805,11 +802,7 @@ document.addEventListener('DOMContentLoaded', () => {
805
802
  return;
806
803
  }
807
804
  if (this.mainTab === 'prompts') {
808
- if (this.promptsSubTab === 'system') {
809
- if (typeof this.loadSystemPrompt === 'function') this.loadSystemPrompt();
810
- } else if (typeof this.loadPromptsContent === 'function') {
811
- this.loadPromptsContent();
812
- }
805
+ if (typeof this.loadPromptsTabContent === 'function') this.loadPromptsTabContent();
813
806
  }
814
807
  },
815
808
  projectClaudeMdPath(newPath) {
@@ -826,10 +819,7 @@ document.addEventListener('DOMContentLoaded', () => {
826
819
  if (typeof this.persistWebUiPreferences === 'function') {
827
820
  this.persistWebUiPreferences({ sysPromptMode: newVal });
828
821
  }
829
- }
830
- },
831
-
832
- watch: {
822
+ },
833
823
  configMode(newMode) {
834
824
  if (newMode === 'pi' && typeof this.loadPiSources === 'function') {
835
825
  this.loadPiSources();
@@ -471,6 +471,18 @@ export function createAgentsMethods(options = {}) {
471
471
  .filter(Boolean)
472
472
  .filter(function (v, i, a) { return a.indexOf(v) === i; });
473
473
  }
474
+ if (this.promptsSubTab === 'claude-project'
475
+ && this.mainTab === 'prompts'
476
+ && Array.isArray(this.projectPathOptions)
477
+ && this.projectPathOptions.length) {
478
+ const currentPath = (this.projectClaudeMdPath || '').trim();
479
+ if (currentPath && this.projectPathOptions.indexOf(currentPath) !== -1) {
480
+ this.projectClaudeMdPath = currentPath;
481
+ } else {
482
+ this.projectClaudeMdPath = this.projectPathOptions[0];
483
+ }
484
+ if (typeof this.loadPromptsContent === 'function') this.loadPromptsContent();
485
+ }
474
486
  } catch (_) {
475
487
  // silent
476
488
  } finally {
@@ -980,17 +992,28 @@ export function createAgentsMethods(options = {}) {
980
992
  this.loadProjectPathOptions();
981
993
  }
982
994
  if (this.promptsSubTab === normalized) {
983
- if (normalized === 'system') this.loadSystemPrompt();
984
- else this.loadPromptsContent();
995
+ if (typeof this.loadPromptsTabContent === 'function') this.loadPromptsTabContent();
985
996
  return;
986
997
  }
998
+ this.__skipNextPromptsSubTabLoad = true;
987
999
  this.promptsSubTab = normalized;
988
- if (normalized === 'system') this.loadSystemPrompt();
1000
+ if (typeof this.loadPromptsTabContent === 'function') this.loadPromptsTabContent();
989
1001
  this.$nextTick(() => {
990
1002
  document.querySelector('.main-panel')?.scrollTo({ top: 0, left: 0, behavior: 'auto' });
991
1003
  });
992
1004
  },
993
1005
 
1006
+ loadPromptsTabContent() {
1007
+ if (this.promptsSubTab === 'system') {
1008
+ if (typeof this.loadSystemPrompt === 'function') this.loadSystemPrompt();
1009
+ } else {
1010
+ if (this.promptsSubTab === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading && typeof this.loadProjectPathOptions === 'function') {
1011
+ this.loadProjectPathOptions();
1012
+ }
1013
+ if (typeof this.loadPromptsContent === 'function') this.loadPromptsContent();
1014
+ }
1015
+ },
1016
+
994
1017
  async loadPromptsContent() {
995
1018
  const requestToken = issueLatestRequestToken(this, '_agentsOpenRequestToken');
996
1019
  this.agentsLoading = true;
@@ -1004,7 +1027,11 @@ export function createAgentsMethods(options = {}) {
1004
1027
  if (!this.projectPathOptions.length && !this.projectPathOptionsLoading && typeof this.loadProjectPathOptions === 'function') {
1005
1028
  this.loadProjectPathOptions();
1006
1029
  }
1007
- const projectPath = (this.projectClaudeMdPath || '').trim();
1030
+ let projectPath = (this.projectClaudeMdPath || '').trim();
1031
+ if (!projectPath && Array.isArray(this.projectPathOptions) && this.projectPathOptions.length) {
1032
+ this.projectClaudeMdPath = this.projectPathOptions[0];
1033
+ projectPath = this.projectClaudeMdPath.trim();
1034
+ }
1008
1035
  if (projectPath) {
1009
1036
  rpcParams.baseDir = projectPath;
1010
1037
  }
@@ -81,24 +81,13 @@ export function createPiConfigMethods({ api: apiClient }) {
81
81
  this.piSaving = true;
82
82
  try {
83
83
  const providerId = this.editingPiProvider.id;
84
- const values = this.editingPiProvider.form;
85
- const extras = this.editingPiProvider.extras || {};
86
- const provider = this.piProviders[providerId] || {};
87
-
88
- const merged = {
89
- ...extras,
90
- baseUrl: values.baseUrl,
91
- api: values.api,
92
- apiKey: values.apiKey || '',
93
- models: (Array.isArray(values.models) ? values.models : []).map((model) => ({
94
- ...model,
95
- id: model.id || '',
96
- name: model.id || ''
97
- }))
98
- };
99
- if (isPiPlainObject(provider.headers)) merged.headers = provider.headers;
100
- if (typeof provider.title === 'string') merged.title = provider.title;
101
- else if (typeof provider.name === 'string') merged.name = provider.name;
84
+ const { merged, ok, error } = this.piBuildMergedEditorRecord();
85
+ if (!ok || !merged) {
86
+ this.piEditorJsonError = error || '配置 JSON 不合法';
87
+ this.message = this.piEditorJsonError;
88
+ this.messageType = 'error';
89
+ return;
90
+ }
102
91
 
103
92
  await this.persistPiProvider(providerId, merged);
104
93
  this.piProviders = { ...this.piProviders, [providerId]: merged };
@@ -122,6 +111,8 @@ export function createPiConfigMethods({ api: apiClient }) {
122
111
  const providerId = this.derivePiProviderId(baseUrl);
123
112
  const modelId = (this.addingPiProviderModel || '').trim();
124
113
  const values = {
114
+ id: providerId,
115
+ name: providerId,
125
116
  baseUrl,
126
117
  api: this.addingPiProviderApi || 'openai-completions',
127
118
  apiKey: (this.addingPiProviderApiKey || '').trim(),
@@ -151,18 +142,12 @@ export function createPiConfigMethods({ api: apiClient }) {
151
142
  }
152
143
  },
153
144
  derivePiProviderId(baseUrl) {
154
- let base = '';
155
- try {
156
- base = new URL(baseUrl).hostname.replace(/^www\./, '');
157
- } catch (_) {
158
- base = '';
159
- }
160
- const candidate = (base || 'pi-provider').replace(/[^a-zA-Z0-9-]/g, '-')
161
- .replace(/-+/g, '-').replace(/^-|-$/g, '') || 'pi-provider';
162
- if (!this.piProviders[candidate]) return candidate;
163
- let suffix = 2;
164
- while (this.piProviders[`${candidate}-${suffix}`]) suffix += 1;
165
- return `${candidate}-${suffix}`;
145
+ let candidate = String(Date.now());
146
+ while (this.piProviders[candidate]) candidate = String(Number(candidate) + 1);
147
+ return candidate;
148
+ },
149
+ piProviderUrlTitle() {
150
+ return '';
166
151
  },
167
152
  startAddPiProvider() {
168
153
  if (!this.isToolConfigWriteAllowed('pi') || this.piSaving) return;
@@ -236,6 +221,7 @@ export function createPiConfigMethods({ api: apiClient }) {
236
221
  this.piRemoteModels = [];
237
222
  this.piRemoteModelError = '';
238
223
  this.piModelSearch = '';
224
+ this.piEditorJsonError = '';
239
225
  const provider = this.piProviders[providerId] || {};
240
226
  const knownKeys = ['id', 'name', 'title', 'baseUrl', 'api', 'apiKey', 'models', 'headers', 'configJson'];
241
227
  const extras = {};
@@ -273,6 +259,7 @@ export function createPiConfigMethods({ api: apiClient }) {
273
259
  this.piRemoteModels = [];
274
260
  this.piRemoteModelError = '';
275
261
  this.piModelSearch = '';
262
+ this.piEditorJsonError = '';
276
263
  },
277
264
  removePiProviderModel(index) {
278
265
  if (!this.editingPiProvider) return;
@@ -346,20 +333,7 @@ export function createPiConfigMethods({ api: apiClient }) {
346
333
  this.messageType = 'success';
347
334
  },
348
335
  piProviderName(providerId) {
349
- const provider = this.piProviders[providerId] || {};
350
- if (provider.title || provider.name) return provider.title || provider.name;
351
- const urlTitle = this.piProviderUrlTitle(provider);
352
- return urlTitle || providerId;
353
- },
354
- piProviderUrlTitle(provider) {
355
- const baseUrl = provider && provider.baseUrl ? String(provider.baseUrl) : '';
356
- if (!baseUrl) return '';
357
- try {
358
- const hostname = new URL(baseUrl).hostname;
359
- return hostname.replace(/^www\./, '');
360
- } catch (e) {
361
- return '';
362
- }
336
+ return String(providerId || '');
363
337
  },
364
338
  piProviderSummary(providerId) {
365
339
  const provider = this.piProviders[providerId] || {};
@@ -367,5 +341,57 @@ export function createPiConfigMethods({ api: apiClient }) {
367
341
  const api = provider.api || '';
368
342
  return [baseUrl, api].filter(Boolean).join(' • ') || 'Piper provider';
369
343
  }
344
+ ,
345
+ piParseEditorJsonDraft() {
346
+ const provider = this.editingPiProvider;
347
+ const draft = provider && provider.form ? String(provider.form.configJsonDraft || '').trim() : '';
348
+ if (!draft) return { ok: true, extras: {} };
349
+ try {
350
+ const parsed = JSON.parse(draft);
351
+ if (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object') {
352
+ return { ok: false, extras: {}, error: '配置 JSON 必须是对象' };
353
+ }
354
+ return { ok: true, extras: parsed };
355
+ } catch (e) {
356
+ return { ok: false, extras: {}, error: e && e.message ? '配置 JSON 解析失败:' + e.message : '配置 JSON 解析失败' };
357
+ }
358
+ },
359
+ piBuildMergedEditorRecord() {
360
+ const provider = this.editingPiProvider;
361
+ if (!provider || !provider.form) return { merged: null, ok: false, error: '未在编辑供应商', extras: {} };
362
+ const draft = this.piParseEditorJsonDraft();
363
+ if (!draft.ok) return { merged: null, ok: false, error: draft.error, extras: {} };
364
+ const values = provider.form || {};
365
+ const extras = draft.extras || {};
366
+ const orig = this.piProviders[provider.id] || {};
367
+ const mapModels = (xs) => (Array.isArray(xs) ? xs : []).map((model) => ({
368
+ ...model,
369
+ id: (model && model.id) || '',
370
+ name: (model && (model.name || model.id)) || ''
371
+ }));
372
+ const merged = { ...extras };
373
+ if (merged.baseUrl === undefined) merged.baseUrl = values.baseUrl || '';
374
+ if (merged.api === undefined) merged.api = values.api || '';
375
+ if (merged.apiKey === undefined) merged.apiKey = values.apiKey || '';
376
+ if (merged.models === undefined) merged.models = mapModels(values.models);
377
+ if (isPiPlainObject(orig.headers)) merged.headers = orig.headers;
378
+ if (typeof orig.title === 'string') merged.title = orig.title;
379
+ else if (typeof orig.name === 'string') merged.name = orig.name;
380
+ if (isPiPlainObject(orig.configJson)) merged.configJson = orig.configJson;
381
+ return { merged, ok: true, error: '', extras };
382
+ },
383
+ piProviderPreviewJson() {
384
+ const { merged, ok } = this.piBuildMergedEditorRecord();
385
+ if (!ok || !merged) return '{}';
386
+ try {
387
+ return JSON.stringify(merged, null, 2);
388
+ } catch (_) {
389
+ return '{}';
390
+ }
391
+ },
392
+ piEditorJsonDraftInput() {
393
+ const draft = this.piParseEditorJsonDraft();
394
+ this.piEditorJsonError = draft.ok ? '' : draft.error;
395
+ }
370
396
  };
371
397
  }
@@ -1,4 +1,4 @@
1
- export const PROVIDER_CONFIG_MODE_META = Object.freeze({
1
+ export const PROVIDER_CONFIG_MODE_META = Object.freeze({
2
2
  codex: Object.freeze({
3
3
  label: 'Codex',
4
4
  modelPlaceholder: '例如: gpt-5.3-codex',
@@ -148,4 +148,4 @@ export function createConfigModeComputed() {
148
148
  return '未选择';
149
149
  }
150
150
  };
151
- }
151
+ }
@@ -1346,6 +1346,10 @@ const en = Object.freeze({
1346
1346
  'pi.providers.fields.apiKey': 'API Key',
1347
1347
  'pi.providers.fields.models': 'Models',
1348
1348
  'pi.providers.fields.configJson': 'Config JSON',
1349
+ 'pi.providers.json.title': 'Config JSON',
1350
+ 'pi.providers.json.desc': 'Live preview of the final provider record merged from form changes and extras; edits are merged into models.json on save.',
1351
+ 'pi.providers.json.previewTitle': 'Preview',
1352
+ 'pi.providers.json.editTitle': 'Edit JSON',
1349
1353
  'pi.providers.models.remove': 'Remove',
1350
1354
  'pi.providers.models.search': 'Search available models…',
1351
1355
  'pi.providers.models.remoteCount': '{count} models available, click to add',
@@ -1347,6 +1347,10 @@ const ja = Object.freeze({
1347
1347
  'pi.providers.fields.apiKey': 'API Key',
1348
1348
  'pi.providers.fields.models': 'モデル',
1349
1349
  'pi.providers.fields.configJson': '設定 JSON',
1350
+ 'pi.providers.json.title': '設定 JSON',
1351
+ 'pi.providers.json.desc': 'フォーム変更と拡張フィールドを統合した最終レコードをリアルタイムにプレビュー。保存時に models.json へ統合します。',
1352
+ 'pi.providers.json.previewTitle': 'プレビュー',
1353
+ 'pi.providers.json.editTitle': 'JSON を編集',
1350
1354
  'pi.providers.models.remove': '削除',
1351
1355
  'pi.providers.models.search': '利用可能なモデルを検索…',
1352
1356
  'pi.providers.models.remoteCount': '{count} 個のモデルを取得しました。クリックで追加',
@@ -1315,6 +1315,10 @@ const vi = Object.freeze({
1315
1315
  'pi.providers.fields.apiKey': 'API Key',
1316
1316
  'pi.providers.fields.models': 'Mô hình',
1317
1317
  'pi.providers.fields.configJson': 'Cấu hình JSON',
1318
+ 'pi.providers.json.title': 'Cấu hình JSON',
1319
+ 'pi.providers.json.desc': 'Xem trước bản ghi cuối từ thay đổi biểu mẫu; các chỉnh sửa được gộp vào models.json khi lưu.',
1320
+ 'pi.providers.json.previewTitle': 'Xem trước',
1321
+ 'pi.providers.json.editTitle': 'Chỉnh sửa JSON',
1318
1322
  'pi.providers.models.remove': 'Xóa',
1319
1323
  'pi.providers.models.search': 'Tìm kiếm mô hình khả dụng…',
1320
1324
  'pi.providers.models.remoteCount': 'Đã lấy {count} mô hình, bấm để thêm',
@@ -1344,6 +1344,10 @@ const zhTw = Object.freeze({
1344
1344
  'pi.providers.fields.apiKey': 'API Key',
1345
1345
  'pi.providers.fields.models': '模型',
1346
1346
  'pi.providers.fields.configJson': '組態 JSON',
1347
+ 'pi.providers.json.title': '設定 JSON',
1348
+ 'pi.providers.json.desc': '即時預覽表單異動合併後的最終紀錄;儲存時併入 models.json。',
1349
+ 'pi.providers.json.previewTitle': '預覽',
1350
+ 'pi.providers.json.editTitle': '編輯 JSON',
1347
1351
  'pi.providers.models.remove': '刪除',
1348
1352
  'pi.providers.models.search': '搜尋可用模型…',
1349
1353
  'pi.providers.models.remoteCount': '已取得 {count} 個可用模型,點擊新增',
@@ -1344,6 +1344,10 @@ const zh = Object.freeze({
1344
1344
  'pi.providers.fields.apiKey': 'API Key',
1345
1345
  'pi.providers.fields.models': '模型',
1346
1346
  'pi.providers.fields.configJson': '配置 JSON',
1347
+ 'pi.providers.json.title': '配置 JSON',
1348
+ 'pi.providers.json.desc': '实时预览表单改动合并后的最终记录;保存时并入 models.json。',
1349
+ 'pi.providers.json.previewTitle': '预览',
1350
+ 'pi.providers.json.editTitle': '编辑 JSON',
1347
1351
  'pi.providers.models.remove': '删除',
1348
1352
  'pi.providers.models.search': '搜索可用模型…',
1349
1353
  'pi.providers.models.remoteCount': '已拉取 {count} 个可用模型,点击添加',
@@ -1,4 +1,4 @@
1
- <div id="app" class="container" v-cloak>
1
+ <div id="app" class="container" v-cloak>
2
2
  <div v-if="!sessionStandalone" class="mobile-brand-bar">
3
3
  <div class="mobile-brand-title">Codex Mate<span v-if="appVersion" class="brand-version"> v{{ appVersion }}</span></div>
4
4
  <button
@@ -569,4 +569,4 @@
569
569
  </div>
570
570
 
571
571
  <!-- 内容包裹器 - 稳定布局 -->
572
- <div class="content-wrapper">
572
+ <div class="content-wrapper">
@@ -117,6 +117,25 @@
117
117
  <div class="settings-tab-actions">
118
118
  <button type="button" class="btn-tool" @click="confirmDeletePiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.delete') }}</button>
119
119
  </div>
120
+
121
+ <section class="selector-section">
122
+ <div class="selector-header">
123
+ <span class="selector-title">{{ t('pi.providers.json.title') }}</span>
124
+ </div>
125
+ <p class="config-template-hint">{{ t('pi.providers.json.desc') }}</p>
126
+ <label class="form-label">{{ t('pi.providers.json.previewTitle') }}</label>
127
+ <pre class="config-json-preview">{{ piProviderPreviewJson() }}</pre>
128
+ <label class="form-label">{{ t('pi.providers.json.editTitle') }}</label>
129
+ <textarea
130
+ class="template-textarea config-json-editor"
131
+ v-model="editingPiProvider.form.configJsonDraft"
132
+ spellcheck="false"
133
+ autocomplete="off"
134
+ :disabled="!isToolConfigWriteAllowed('pi')"
135
+ @input="piEditorJsonDraftInput"
136
+ placeholder="{}"></textarea>
137
+ <p v-if="piEditorJsonError" class="config-template-hint error-text">{{ piEditorJsonError }}</p>
138
+ </section>
120
139
  </section>
121
140
 
122
141
  <section class="selector-section" v-if="showAddPiProviderModal">
@@ -168,4 +187,4 @@
168
187
  </div>
169
188
  </div>
170
189
  </div>
171
- </div>
190
+ </div>
@@ -3458,6 +3458,32 @@ return function render(_ctx, _cache) {
3458
3458
  onClick: _ctx.confirmDeletePiProvider,
3459
3459
  disabled: !_ctx.isToolConfigWriteAllowed('pi') || _ctx.piSaving
3460
3460
  }, _toDisplayString(_ctx.t('pi.providers.delete')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
3461
+ ]),
3462
+ _createElementVNode("section", { class: "selector-section" }, [
3463
+ _createElementVNode("div", { class: "selector-header" }, [
3464
+ _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('pi.providers.json.title')), 1 /* TEXT */)
3465
+ ]),
3466
+ _createElementVNode("p", { class: "config-template-hint" }, _toDisplayString(_ctx.t('pi.providers.json.desc')), 1 /* TEXT */),
3467
+ _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t('pi.providers.json.previewTitle')), 1 /* TEXT */),
3468
+ _createElementVNode("pre", { class: "config-json-preview" }, _toDisplayString(_ctx.piProviderPreviewJson()), 1 /* TEXT */),
3469
+ _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t('pi.providers.json.editTitle')), 1 /* TEXT */),
3470
+ _withDirectives(_createElementVNode("textarea", {
3471
+ class: "template-textarea config-json-editor",
3472
+ "onUpdate:modelValue": $event => ((_ctx.editingPiProvider.form.configJsonDraft) = $event),
3473
+ spellcheck: "false",
3474
+ autocomplete: "off",
3475
+ disabled: !_ctx.isToolConfigWriteAllowed('pi'),
3476
+ onInput: _ctx.piEditorJsonDraftInput,
3477
+ placeholder: "{}"
3478
+ }, null, 40 /* PROPS, NEED_HYDRATION */, ["onUpdate:modelValue", "disabled", "onInput"]), [
3479
+ [_vModelText, _ctx.editingPiProvider.form.configJsonDraft]
3480
+ ]),
3481
+ (_ctx.piEditorJsonError)
3482
+ ? (_openBlock(), _createElementBlock("p", {
3483
+ key: 0,
3484
+ class: "config-template-hint error-text"
3485
+ }, _toDisplayString(_ctx.piEditorJsonError), 1 /* TEXT */))
3486
+ : _createCommentVNode("v-if", true)
3461
3487
  ])
3462
3488
  ]))
3463
3489
  : _createCommentVNode("v-if", true),
@@ -352,6 +352,28 @@
352
352
  margin-top: 2px;
353
353
  }
354
354
 
355
+ .config-json-preview {
356
+ width: 100%;
357
+ max-height: 240px;
358
+ overflow: auto;
359
+ margin: 0 0 10px;
360
+ padding: 12px;
361
+ border: 1px solid var(--color-border);
362
+ border-radius: var(--radius-md);
363
+ background: rgba(0, 0, 0, 0.04);
364
+ color: var(--color-text-secondary);
365
+ font-family: var(--font-mono);
366
+ font-size: 12px;
367
+ line-height: 1.55;
368
+ white-space: pre;
369
+ box-shadow: var(--shadow-subtle);
370
+ }
371
+
372
+ .config-json-editor {
373
+ min-height: 180px;
374
+ margin-bottom: 8px;
375
+ }
376
+
355
377
  /* ============================================
356
378
  按钮
357
379
  ============================================ */