codexmate 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +389 -284
  2. package/README.zh.md +321 -0
  3. package/cli/agents-files.js +224 -162
  4. package/cli/archive-helpers.js +446 -446
  5. package/cli/auth-profiles.js +359 -359
  6. package/cli/builtin-proxy.js +1044 -580
  7. package/cli/claude-proxy.js +998 -998
  8. package/cli/config-bootstrap.js +384 -384
  9. package/cli/config-health.js +338 -338
  10. package/cli/openai-bridge.js +950 -0
  11. package/cli/openclaw-config.js +629 -629
  12. package/cli/session-usage.concurrent.js +28 -0
  13. package/cli/session-usage.js +112 -0
  14. package/cli/session-usage.models.js +176 -0
  15. package/cli/skills.js +1141 -1141
  16. package/cli/zip-commands.js +510 -510
  17. package/cli.js +13214 -13129
  18. package/lib/cli-file-utils.js +151 -151
  19. package/lib/cli-models-utils.js +419 -419
  20. package/lib/cli-network-utils.js +164 -164
  21. package/lib/cli-path-utils.js +69 -69
  22. package/lib/cli-session-utils.js +121 -121
  23. package/lib/cli-sessions.js +386 -386
  24. package/lib/cli-utils.js +155 -155
  25. package/lib/download-artifacts.js +77 -77
  26. package/lib/mcp-stdio.js +440 -440
  27. package/lib/task-orchestrator.js +869 -869
  28. package/lib/text-diff.js +303 -303
  29. package/lib/workflow-engine.js +340 -340
  30. package/package.json +74 -74
  31. package/res/json5.min.js +1 -1
  32. package/res/logo.png +0 -0
  33. package/res/vue.global.prod.js +13 -13
  34. package/web-ui/app.js +575 -530
  35. package/web-ui/index.html +34 -33
  36. package/web-ui/logic.agents-diff.mjs +386 -386
  37. package/web-ui/logic.claude.mjs +168 -168
  38. package/web-ui/logic.mjs +5 -5
  39. package/web-ui/logic.runtime.mjs +128 -124
  40. package/web-ui/logic.sessions.mjs +614 -581
  41. package/web-ui/modules/api.mjs +90 -90
  42. package/web-ui/modules/app.computed.dashboard.mjs +126 -113
  43. package/web-ui/modules/app.computed.index.mjs +17 -15
  44. package/web-ui/modules/app.computed.main-tabs.mjs +198 -195
  45. package/web-ui/modules/app.computed.session.mjs +653 -507
  46. package/web-ui/modules/app.constants.mjs +15 -15
  47. package/web-ui/modules/app.methods.agents.mjs +544 -493
  48. package/web-ui/modules/app.methods.claude-config.mjs +174 -174
  49. package/web-ui/modules/app.methods.codex-config.mjs +795 -640
  50. package/web-ui/modules/app.methods.index.mjs +92 -88
  51. package/web-ui/modules/app.methods.install.mjs +161 -149
  52. package/web-ui/modules/app.methods.navigation.mjs +619 -619
  53. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -814
  54. package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -372
  55. package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -369
  56. package/web-ui/modules/app.methods.providers.mjs +404 -363
  57. package/web-ui/modules/app.methods.runtime.mjs +323 -323
  58. package/web-ui/modules/app.methods.session-actions.mjs +537 -520
  59. package/web-ui/modules/app.methods.session-browser.mjs +626 -626
  60. package/web-ui/modules/app.methods.session-timeline.mjs +448 -448
  61. package/web-ui/modules/app.methods.session-trash.mjs +422 -422
  62. package/web-ui/modules/app.methods.startup-claude.mjs +405 -412
  63. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -471
  64. package/web-ui/modules/config-mode.computed.mjs +126 -126
  65. package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
  66. package/web-ui/modules/i18n.mjs +1609 -0
  67. package/web-ui/modules/plugins.computed.mjs +220 -0
  68. package/web-ui/modules/plugins.methods.mjs +620 -0
  69. package/web-ui/modules/plugins.storage.mjs +37 -0
  70. package/web-ui/modules/skills.computed.mjs +107 -107
  71. package/web-ui/modules/skills.methods.mjs +481 -481
  72. package/web-ui/partials/index/layout-footer.html +13 -13
  73. package/web-ui/partials/index/layout-header.html +461 -402
  74. package/web-ui/partials/index/modal-config-template-agents.html +175 -125
  75. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  76. package/web-ui/partials/index/modal-health-check.html +72 -72
  77. package/web-ui/partials/index/modal-openclaw-config.html +280 -280
  78. package/web-ui/partials/index/modal-skills.html +200 -184
  79. package/web-ui/partials/index/modals-basic.html +165 -156
  80. package/web-ui/partials/index/panel-config-claude.html +138 -126
  81. package/web-ui/partials/index/panel-config-codex.html +234 -237
  82. package/web-ui/partials/index/panel-config-openclaw.html +78 -78
  83. package/web-ui/partials/index/panel-docs.html +147 -130
  84. package/web-ui/partials/index/panel-market.html +174 -174
  85. package/web-ui/partials/index/panel-orchestration.html +397 -397
  86. package/web-ui/partials/index/panel-plugins.html +243 -0
  87. package/web-ui/partials/index/panel-sessions.html +292 -292
  88. package/web-ui/partials/index/panel-settings.html +258 -190
  89. package/web-ui/partials/index/panel-usage.html +353 -213
  90. package/web-ui/session-helpers.mjs +573 -559
  91. package/web-ui/source-bundle.cjs +233 -233
  92. package/web-ui/styles/base-theme.css +264 -271
  93. package/web-ui/styles/controls-forms.css +362 -360
  94. package/web-ui/styles/docs-panel.css +247 -182
  95. package/web-ui/styles/feedback.css +108 -108
  96. package/web-ui/styles/health-check-dialog.css +144 -144
  97. package/web-ui/styles/layout-shell.css +596 -376
  98. package/web-ui/styles/modals-core.css +464 -464
  99. package/web-ui/styles/navigation-panels.css +382 -348
  100. package/web-ui/styles/openclaw-structured.css +266 -266
  101. package/web-ui/styles/plugins-panel.css +518 -0
  102. package/web-ui/styles/responsive.css +456 -450
  103. package/web-ui/styles/sessions-list.css +400 -400
  104. package/web-ui/styles/sessions-preview.css +411 -411
  105. package/web-ui/styles/sessions-toolbar-trash.css +268 -243
  106. package/web-ui/styles/sessions-usage.css +851 -628
  107. package/web-ui/styles/settings-panel.css +166 -0
  108. package/web-ui/styles/skills-list.css +303 -296
  109. package/web-ui/styles/skills-market.css +396 -335
  110. package/web-ui/styles/task-orchestration.css +776 -776
  111. package/web-ui/styles/titles-cards.css +408 -408
  112. package/web-ui/styles.css +20 -18
  113. package/web-ui.html +17 -17
  114. package/README.en.md +0 -349
@@ -1,190 +1,258 @@
1
- <!-- 设置面板 -->
2
- <div
3
- v-show="mainTab === 'settings'"
4
- class="mode-content"
5
- id="panel-settings"
6
- role="tabpanel"
7
- :aria-labelledby="'tab-settings'">
8
- <div class="config-subtabs settings-subtabs" role="tablist" aria-label="设置子标签">
9
- <button
10
- id="settings-tab-backup"
11
- role="tab"
12
- aria-controls="settings-panel-backup"
13
- :aria-selected="settingsTab === 'backup'"
14
- :tabindex="settingsTab === 'backup' ? 0 : -1"
15
- :class="['config-subtab', { active: settingsTab === 'backup' }]"
16
- @click="onSettingsTabClick('backup')">
17
- 备份与导入
18
- </button>
19
- <button
20
- id="settings-tab-trash"
21
- role="tab"
22
- aria-controls="settings-panel-trash"
23
- :aria-selected="settingsTab === 'trash'"
24
- :tabindex="settingsTab === 'trash' ? 0 : -1"
25
- :class="['config-subtab', { active: settingsTab === 'trash' }]"
26
- @click="onSettingsTabClick('trash')">
27
- 回收站
28
- <span class="settings-tab-badge">{{ sessionTrashCount }}</span>
29
- </button>
30
- <button
31
- id="settings-tab-device"
32
- role="tab"
33
- aria-controls="settings-panel-device"
34
- :aria-selected="settingsTab === 'device'"
35
- :tabindex="settingsTab === 'device' ? 0 : -1"
36
- :class="['config-subtab', { active: settingsTab === 'device' }]"
37
- @click="onSettingsTabClick('device')">
38
- 设备
39
- </button>
40
- </div>
41
-
42
- <div
43
- v-show="settingsTab === 'backup'"
44
- id="settings-panel-backup"
45
- role="tabpanel"
46
- aria-labelledby="settings-tab-backup">
47
- <div class="selector-section">
48
- <div class="selector-header">
49
- <span class="selector-title">分享命令前缀</span>
50
- </div>
51
- <select class="model-select" :value="shareCommandPrefix" @change="setShareCommandPrefix($event.target.value)">
52
- <option value="npm start">npm start</option>
53
- <option value="codexmate">codexmate</option>
54
- </select>
55
- <div class="config-template-hint">
56
- 默认走项目内 <code>npm start</code>,也可切到全局 <code>codexmate</code>。该设置会缓存到浏览器本地。
57
- </div>
58
- </div>
59
- <div class="selector-section">
60
- <div class="selector-header">
61
- <span class="selector-title">Claude 配置</span>
62
- </div>
63
- <button class="btn-tool" @click="downloadClaudeDirectory" :disabled="claudeDownloadLoading">
64
- {{ claudeDownloadLoading ? ('备份中 ' + claudeDownloadProgress + '%') : '一键备份 ~/.claude' }}
65
- </button>
66
- <button class="btn-tool" @click="triggerClaudeImport" :disabled="claudeImportLoading">
67
- {{ claudeImportLoading ? '导入中...' : '导入 ~/.claude 备份' }}
68
- </button>
69
- <input
70
- ref="claudeImportInput"
71
- class="sr-only"
72
- type="file"
73
- accept=".zip"
74
- @change="handleClaudeImportChange">
75
- </div>
76
- <div class="selector-section">
77
- <div class="selector-header">
78
- <span class="selector-title">Codex 配置</span>
79
- </div>
80
- <button class="btn-tool" @click="downloadCodexDirectory" :disabled="codexDownloadLoading">
81
- {{ codexDownloadLoading ? ('备份中 ' + codexDownloadProgress + '%') : '一键备份 ~/.codex' }}
82
- </button>
83
- <button class="btn-tool" @click="triggerCodexImport" :disabled="codexImportLoading">
84
- {{ codexImportLoading ? '导入中...' : '导入 ~/.codex 备份' }}
85
- </button>
86
- <input
87
- ref="codexImportInput"
88
- class="sr-only"
89
- type="file"
90
- accept=".zip"
91
- @change="handleCodexImportChange">
92
- </div>
93
- </div>
94
-
95
- <div
96
- v-show="settingsTab === 'trash'"
97
- id="settings-panel-trash"
98
- role="tabpanel"
99
- aria-labelledby="settings-tab-trash">
100
- <div class="selector-section">
101
- <div class="selector-header">
102
- <span class="selector-title">会话删除行为</span>
103
- </div>
104
- <label class="health-remote-toggle">
105
- <input type="checkbox" :checked="sessionTrashEnabled" @change="setSessionTrashEnabled($event.target.checked)">
106
- <span>删除会话时先移入回收站</span>
107
- </label>
108
- <div class="config-template-hint">
109
- 默认开启。关闭后,会话浏览里的删除会直接永久删除,不再进入回收站。
110
- </div>
111
- </div>
112
- <div class="selector-section">
113
- <div class="selector-header settings-tab-header">
114
- <div class="settings-tab-actions trash-header-actions">
115
- <button class="btn-tool btn-tool-compact" @click="loadSessionTrash({ forceRefresh: true })" :disabled="sessionTrashLoading || sessionTrashClearing">
116
- {{ sessionTrashLoading ? '刷新中...' : '刷新列表' }}
117
- </button>
118
- <button class="btn-tool btn-tool-compact" @click="clearSessionTrash" :disabled="sessionTrashClearing || sessionTrashLoading || !(Number(sessionTrashCount) > 0)">
119
- {{ sessionTrashClearing ? '清空中...' : '清空回收站' }}
120
- </button>
121
- </div>
122
- </div>
123
-
124
- <div v-if="getSessionTrashViewState() === 'loading'" class="session-empty">
125
- 正在加载回收站...
126
- </div>
127
- <div v-else-if="getSessionTrashViewState() === 'empty'" class="session-empty">
128
- 回收站为空
129
- </div>
130
- <div v-else-if="getSessionTrashViewState() === 'retry'" class="session-empty">
131
- 回收站列表加载失败,请刷新重试
132
- </div>
133
- <div v-else class="trash-list">
134
- <div v-for="item in visibleSessionTrashItems" :key="item.trashId" class="trash-item session-item session-card">
135
- <div class="trash-item-header session-item-header">
136
- <div class="trash-item-main">
137
- <div class="trash-item-mainline">
138
- <div class="trash-item-title">{{ item.title || item.sessionId }}</div>
139
- <span class="session-count-badge">{{ item.messageCount ?? 0 }}</span>
140
- </div>
141
- <div class="trash-item-meta session-item-meta">
142
- <span class="session-source">{{ item.sourceLabel }}</span>
143
- </div>
144
- </div>
145
- <div class="trash-item-side">
146
- <div class="trash-item-actions session-item-actions">
147
- <button class="btn-mini" @click="restoreSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
148
- {{ sessionTrashRestoring[getSessionTrashActionKey(item)] ? '恢复中...' : '恢复' }}
149
- </button>
150
- <button class="btn-mini delete" @click="purgeSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
151
- {{ sessionTrashPurging[getSessionTrashActionKey(item)] ? '删除中...' : '彻底删除' }}
152
- </button>
153
- </div>
154
- <div class="trash-item-time session-item-time">{{ item.deletedAt || item.updatedAt || 'unknown time' }}</div>
155
- </div>
156
- </div>
157
- <div v-if="item.cwd" class="trash-item-path session-item-sub session-item-wrap">
158
- <span class="trash-item-label">工作区</span>
159
- <span>{{ item.cwd }}</span>
160
- </div>
161
- <div class="trash-item-path session-item-sub session-item-wrap">
162
- <span class="trash-item-label">原文件</span>
163
- <span>{{ item.originalFilePath }}</span>
164
- </div>
165
- </div>
166
- <div v-if="sessionTrashHasMoreItems" class="trash-list-footer">
167
- <button class="btn-tool btn-tool-compact" @click="loadMoreSessionTrashItems" :disabled="sessionTrashLoading || sessionTrashClearing">
168
- 加载更多(剩余 {{ sessionTrashHiddenCount }} 项)
169
- </button>
170
- </div>
171
- </div>
172
- </div>
173
- </div>
174
-
175
- <div
176
- v-show="settingsTab === 'device'"
177
- id="settings-panel-device"
178
- role="tabpanel"
179
- aria-labelledby="settings-tab-device">
180
- <div class="selector-section">
181
- <div class="selector-header">
182
- <span class="selector-title">配置重置</span>
183
- </div>
184
- <div class="config-template-hint">先备份 config.toml,再写默认配置。</div>
185
- <button class="btn-tool" @click="resetConfig" :disabled="resetConfigLoading || loading || !!initError">
186
- {{ resetConfigLoading ? '重装中...' : '重装配置' }}
187
- </button>
188
- </div>
189
- </div>
190
- </div>
1
+ <!-- 设置面板 -->
2
+ <div
3
+ v-show="mainTab === 'settings'"
4
+ class="mode-content"
5
+ id="panel-settings"
6
+ role="tabpanel"
7
+ :aria-labelledby="'tab-settings'">
8
+ <div class="config-subtabs settings-subtabs" role="tablist" aria-label="Settings tabs">
9
+ <button
10
+ id="settings-tab-backup"
11
+ role="tab"
12
+ aria-controls="settings-panel-backup"
13
+ :aria-selected="settingsTab === 'backup'"
14
+ :tabindex="settingsTab === 'backup' ? 0 : -1"
15
+ :class="['config-subtab', { active: settingsTab === 'backup' }]"
16
+ @click="onSettingsTabClick('backup')">
17
+ {{ t('settings.tab.backup') }}
18
+ </button>
19
+ <button
20
+ id="settings-tab-trash"
21
+ role="tab"
22
+ aria-controls="settings-panel-trash"
23
+ :aria-selected="settingsTab === 'trash'"
24
+ :tabindex="settingsTab === 'trash' ? 0 : -1"
25
+ :class="['config-subtab', { active: settingsTab === 'trash' }]"
26
+ @click="onSettingsTabClick('trash')">
27
+ {{ t('settings.tab.trash') }}
28
+ <span class="settings-tab-badge">{{ sessionTrashCount }}</span>
29
+ </button>
30
+ <button
31
+ id="settings-tab-device"
32
+ role="tab"
33
+ aria-controls="settings-panel-device"
34
+ :aria-selected="settingsTab === 'device'"
35
+ :tabindex="settingsTab === 'device' ? 0 : -1"
36
+ :class="['config-subtab', { active: settingsTab === 'device' }]"
37
+ @click="onSettingsTabClick('device')">
38
+ {{ t('settings.tab.device') }}
39
+ </button>
40
+ </div>
41
+
42
+ <div
43
+ v-show="settingsTab === 'backup'"
44
+ id="settings-panel-backup"
45
+ role="tabpanel"
46
+ aria-labelledby="settings-tab-backup">
47
+ <div class="settings-layout">
48
+ <div class="settings-grid">
49
+ <section class="settings-card settings-card--wide" :aria-label="t('settings.sharePrefix.title')">
50
+ <div class="settings-card-header">
51
+ <div class="settings-card-title">{{ t('settings.sharePrefix.title') }}</div>
52
+ <div class="settings-card-meta">{{ t('settings.sharePrefix.meta') }}</div>
53
+ </div>
54
+ <div class="settings-card-body">
55
+ <div class="settings-field-row">
56
+ <label class="settings-field-label" for="settings-share-prefix">{{ t('settings.sharePrefix.label') }}</label>
57
+ <select
58
+ id="settings-share-prefix"
59
+ class="model-select"
60
+ :value="shareCommandPrefix"
61
+ @change="setShareCommandPrefix($event.target.value)">
62
+ <option value="npm start">npm start</option>
63
+ <option value="codexmate">codexmate</option>
64
+ </select>
65
+ </div>
66
+ <div class="settings-card-hint">
67
+ {{ t('settings.sharePrefix.hint') }}
68
+ </div>
69
+ </div>
70
+ </section>
71
+
72
+ <section class="settings-card" :aria-label="t('settings.claude.title')">
73
+ <div class="settings-card-header">
74
+ <div class="settings-card-title">{{ t('settings.claude.title') }}</div>
75
+ <div class="settings-card-meta">{{ t('settings.claude.meta') }}</div>
76
+ </div>
77
+ <div class="settings-card-body">
78
+ <div class="settings-actions">
79
+ <button class="btn-tool" @click="downloadClaudeDirectory" :disabled="claudeDownloadLoading">
80
+ {{ claudeDownloadLoading ? t('settings.backup.progress', { percent: claudeDownloadProgress }) : t('settings.backup.oneClickClaude') }}
81
+ </button>
82
+ <button class="btn-tool" @click="triggerClaudeImport" :disabled="claudeImportLoading">
83
+ {{ claudeImportLoading ? t('settings.importing') : t('settings.backup.importClaude') }}
84
+ </button>
85
+ </div>
86
+ <input
87
+ ref="claudeImportInput"
88
+ class="sr-only"
89
+ type="file"
90
+ accept=".zip"
91
+ @change="handleClaudeImportChange">
92
+ </div>
93
+ </section>
94
+
95
+ <section class="settings-card" :aria-label="t('settings.codex.title')">
96
+ <div class="settings-card-header">
97
+ <div class="settings-card-title">{{ t('settings.codex.title') }}</div>
98
+ <div class="settings-card-meta">{{ t('settings.codex.meta') }}</div>
99
+ </div>
100
+ <div class="settings-card-body">
101
+ <div class="settings-actions">
102
+ <button class="btn-tool" @click="downloadCodexDirectory" :disabled="codexDownloadLoading">
103
+ {{ codexDownloadLoading ? t('settings.backup.progress', { percent: codexDownloadProgress }) : t('settings.backup.oneClickCodex') }}
104
+ </button>
105
+ <button class="btn-tool" @click="triggerCodexImport" :disabled="codexImportLoading">
106
+ {{ codexImportLoading ? t('settings.importing') : t('settings.backup.importCodex') }}
107
+ </button>
108
+ </div>
109
+ <input
110
+ ref="codexImportInput"
111
+ class="sr-only"
112
+ type="file"
113
+ accept=".zip"
114
+ @change="handleCodexImportChange">
115
+ </div>
116
+ </section>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <div
122
+ v-show="settingsTab === 'trash'"
123
+ id="settings-panel-trash"
124
+ role="tabpanel"
125
+ aria-labelledby="settings-tab-trash">
126
+ <div class="settings-layout">
127
+ <div class="settings-grid">
128
+ <section class="settings-card settings-card--wide" :aria-label="t('settings.deleteBehavior.title')">
129
+ <div class="settings-card-header">
130
+ <div class="settings-card-title">{{ t('settings.deleteBehavior.title') }}</div>
131
+ <div class="settings-card-meta">{{ t('settings.deleteBehavior.meta') }}</div>
132
+ </div>
133
+ <div class="settings-card-body">
134
+ <label class="health-remote-toggle settings-toggle">
135
+ <input type="checkbox" :checked="sessionTrashEnabled" @change="setSessionTrashEnabled($event.target.checked)">
136
+ <span>{{ t('settings.deleteBehavior.toggle') }}</span>
137
+ </label>
138
+ <div class="settings-card-hint">
139
+ {{ t('settings.deleteBehavior.hint') }}
140
+ </div>
141
+ </div>
142
+ </section>
143
+
144
+ <section class="settings-card settings-card--wide" :aria-label="t('settings.trash.title')">
145
+ <div class="settings-card-header settings-card-header-row">
146
+ <div>
147
+ <div class="settings-card-title">{{ t('settings.trash.title') }}</div>
148
+ <div class="settings-card-meta">{{ t('settings.trash.meta') }}</div>
149
+ </div>
150
+ <div class="settings-card-actions">
151
+ <button class="btn-tool btn-tool-compact" @click="loadSessionTrash({ forceRefresh: true })" :disabled="sessionTrashLoading || sessionTrashClearing">
152
+ {{ sessionTrashLoading ? t('settings.trash.refreshing') : t('settings.trash.refresh') }}
153
+ </button>
154
+ <button class="btn-tool btn-tool-compact" @click="clearSessionTrash" :disabled="sessionTrashClearing || sessionTrashLoading || !(Number(sessionTrashCount) > 0)">
155
+ {{ sessionTrashClearing ? t('settings.trash.clearing') : t('settings.trash.clear') }}
156
+ </button>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="settings-card-body">
161
+ <div v-if="getSessionTrashViewState() === 'loading'" class="session-empty">
162
+ {{ t('settings.trash.loading') }}
163
+ </div>
164
+ <div v-else-if="getSessionTrashViewState() === 'empty'" class="session-empty">
165
+ {{ t('settings.trash.empty') }}
166
+ </div>
167
+ <div v-else-if="getSessionTrashViewState() === 'retry'" class="session-empty">
168
+ {{ t('settings.trash.retry') }}
169
+ </div>
170
+ <div v-else class="trash-list">
171
+ <div v-for="item in visibleSessionTrashItems" :key="item.trashId" class="trash-item session-item session-card">
172
+ <div class="trash-item-header session-item-header">
173
+ <div class="trash-item-main">
174
+ <div class="trash-item-mainline">
175
+ <div class="trash-item-title">{{ item.title || item.sessionId }}</div>
176
+ <span class="session-count-badge">{{ item.messageCount == null ? 0 : item.messageCount }}</span>
177
+ </div>
178
+ <div class="trash-item-meta session-item-meta">
179
+ <span class="session-source">{{ item.sourceLabel }}</span>
180
+ </div>
181
+ </div>
182
+ <div class="trash-item-side">
183
+ <div class="trash-item-actions session-item-actions">
184
+ <button class="btn-mini" @click="restoreSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
185
+ {{ sessionTrashRestoring[getSessionTrashActionKey(item)] ? t('settings.trash.restoring') : t('settings.trash.restore') }}
186
+ </button>
187
+ <button class="btn-mini delete" @click="purgeSessionTrash(item)" :disabled="sessionTrashLoading || sessionTrashClearing || isSessionTrashActionBusy(item)">
188
+ {{ sessionTrashPurging[getSessionTrashActionKey(item)] ? t('settings.trash.purging') : t('settings.trash.purge') }}
189
+ </button>
190
+ </div>
191
+ <div class="trash-item-time session-item-time">{{ item.deletedAt || item.updatedAt || t('sessions.unknownTime') }}</div>
192
+ </div>
193
+ </div>
194
+ <div v-if="item.cwd" class="trash-item-path session-item-sub session-item-wrap">
195
+ <span class="trash-item-label">{{ t('settings.trash.workspace') }}</span>
196
+ <span>{{ item.cwd }}</span>
197
+ </div>
198
+ <div class="trash-item-path session-item-sub session-item-wrap">
199
+ <span class="trash-item-label">{{ t('settings.trash.originalFile') }}</span>
200
+ <span>{{ item.originalFilePath }}</span>
201
+ </div>
202
+ </div>
203
+ <div v-if="sessionTrashHasMoreItems" class="trash-list-footer">
204
+ <button class="btn-tool btn-tool-compact" @click="loadMoreSessionTrashItems" :disabled="sessionTrashLoading || sessionTrashClearing">
205
+ {{ t('settings.trash.loadMore', { count: sessionTrashHiddenCount }) }}
206
+ </button>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </section>
211
+ </div>
212
+ </div>
213
+ </div>
214
+
215
+ <div
216
+ v-show="settingsTab === 'device'"
217
+ id="settings-panel-device"
218
+ role="tabpanel"
219
+ aria-labelledby="settings-tab-device">
220
+ <div class="settings-layout">
221
+ <div class="settings-grid">
222
+ <section class="settings-card settings-card--wide" :aria-label="t('settings.templateConfirm.title')">
223
+ <div class="settings-card-header">
224
+ <div class="settings-card-title">{{ t('settings.templateConfirm.title') }}</div>
225
+ <div class="settings-card-meta">{{ t('settings.templateConfirm.meta') }}</div>
226
+ </div>
227
+ <div class="settings-card-body">
228
+ <label class="health-remote-toggle settings-toggle">
229
+ <input
230
+ type="checkbox"
231
+ :checked="configTemplateDiffConfirmEnabled"
232
+ @change="setConfigTemplateDiffConfirmEnabled($event.target.checked)">
233
+ <span>{{ t('settings.templateConfirm.toggle') }}</span>
234
+ </label>
235
+ <div class="settings-card-hint">
236
+ {{ t('settings.templateConfirm.hint') }}
237
+ </div>
238
+ </div>
239
+ </section>
240
+
241
+ <section class="settings-card settings-card--wide settings-card--danger" :aria-label="t('settings.reset.title')">
242
+ <div class="settings-card-header">
243
+ <div class="settings-card-title">{{ t('settings.reset.title') }}</div>
244
+ <div class="settings-card-meta">{{ t('settings.reset.meta') }}</div>
245
+ </div>
246
+ <div class="settings-card-body">
247
+ <div class="settings-card-hint">{{ t('settings.reset.hint') }}</div>
248
+ <div class="settings-actions">
249
+ <button class="btn-tool" @click="resetConfig" :disabled="resetConfigLoading || loading || !!initError">
250
+ {{ resetConfigLoading ? t('settings.reset.loading') : t('settings.reset.button') }}
251
+ </button>
252
+ </div>
253
+ </div>
254
+ </section>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>