Skip to content

Commit 23862c0

Browse files
authored
Merge pull request #143 from itmisx/fix/web-session-delete-notice-141
🐛 fix: web-session-delete
2 parents 6a6e345 + 3eb24b1 commit 23862c0

4 files changed

Lines changed: 93 additions & 11 deletions

File tree

tui/model.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,14 +1178,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
11781178

11791179
case webDeleteSessionMsg:
11801180
// 删会话:流式中拒绝。删的是当前会话则删后切回默认并重载。
1181-
if !m.streaming && m.session != nil {
1181+
// 失败必须回传提示(issue #141):默认会话(rootDir 老会话)不可删,
1182+
// 此前 web 路径静默吞掉错误 → 用户点删除无反应、无报错,以为"删不掉"。
1183+
if m.streaming {
1184+
m.broadcast(web.Event{Kind: "notice", Text: T("session.streaming")})
1185+
} else if m.session != nil {
11821186
wasCurrent := m.session.CurrentConversation() == msg.id
1183-
if err := m.session.DeleteConversation(msg.id); err == nil {
1184-
if wasCurrent {
1185-
m.loadCurrentConversation() // 内部广播 session_loaded + 控制态(含会话列表)
1186-
} else {
1187-
m.broadcastSessions()
1188-
}
1187+
if err := m.session.DeleteConversation(msg.id); err != nil {
1188+
m.broadcast(web.Event{Kind: "notice", Text: T("session.delete.cant_default")})
1189+
} else if wasCurrent {
1190+
m.loadCurrentConversation() // 内部广播 session_loaded + 控制态(含会话列表)
1191+
} else {
1192+
m.broadcastSessions()
11891193
}
11901194
}
11911195
return m, nil

web/static/app.js

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ const I18N = {
7272
'session.delete': '删除',
7373
'session.rename.prompt': '输入新的会话名称:',
7474
'session.delete.confirm': '确定删除这个会话吗?此操作不可撤销。',
75+
'session.delete.default_hint': '默认会话不可删除',
76+
'confirm.ok': '确定',
77+
'confirm.cancel': '取消',
7578
'panel.routing': '模型路由',
7679
'panel.mode': '权限模式',
7780
'panel.sandbox': '沙箱',
@@ -150,6 +153,9 @@ const I18N = {
150153
'session.delete': 'Delete',
151154
'session.rename.prompt': 'Enter a new conversation name:',
152155
'session.delete.confirm': 'Delete this conversation? This cannot be undone.',
156+
'session.delete.default_hint': 'The default conversation cannot be deleted',
157+
'confirm.ok': 'Confirm',
158+
'confirm.cancel': 'Cancel',
153159
'panel.routing': 'Routing',
154160
'panel.mode': 'Permission',
155161
'panel.sandbox': 'Sandbox',
@@ -195,6 +201,8 @@ createApp({
195201
skillInstalling: false, skillMsg: '', skillErr: false,
196202
skillQuery: '', skillResults: [], skillSearching: false, skillSearched: false,
197203
toast: '', toastTimer: null, // 顶部临时提示(如压缩完成)
204+
confirmBox: null, // 自定义确认弹窗:null=关闭;{ text, danger, onYes } 时显示(不用浏览器原生 confirm)
205+
promptBox: null, // 自定义输入弹窗:null=关闭;{ title, value, placeholder, onOk } 时显示(不用浏览器原生 prompt)
198206
input: '',
199207
connected: false,
200208
openIdx: -1, // 当前流式 assistant 消息下标
@@ -517,6 +525,25 @@ createApp({
517525
if (this.toastTimer) clearTimeout(this.toastTimer);
518526
this.toastTimer = setTimeout(() => { this.toast = ''; }, 3500);
519527
},
528+
// askConfirm 弹 app 内自定义确认框(替代浏览器原生 confirm):opts = { text, danger, onYes }。
529+
askConfirm(opts) { this.confirmBox = opts; },
530+
confirmYes() {
531+
const cb = this.confirmBox;
532+
this.confirmBox = null;
533+
if (cb && cb.onYes) cb.onYes();
534+
},
535+
confirmNo() { this.confirmBox = null; },
536+
// askPrompt 弹 app 内自定义输入框(替代浏览器原生 prompt):opts = { title, value, placeholder, onOk }。
537+
askPrompt(opts) {
538+
this.promptBox = { title: '', value: '', placeholder: '', ...opts };
539+
this.$nextTick(() => { if (this.$refs.promptInput) { this.$refs.promptInput.focus(); this.$refs.promptInput.select(); } });
540+
},
541+
promptOk() {
542+
const cb = this.promptBox;
543+
this.promptBox = null;
544+
if (cb && cb.onOk) cb.onOk((cb.value || '').trim());
545+
},
546+
promptCancel() { this.promptBox = null; },
520547
async openSkill() {
521548
this.skillShow = true; this.skillMsg = ''; this.skillTab = 'installed';
522549
this.skillResults = []; this.skillSearched = false; this.skillQuery = '';
@@ -782,12 +809,21 @@ createApp({
782809
toggleMenu(id) { this.menuOpen = this.menuOpen === id ? null : id; },
783810
renameSession(s) {
784811
this.menuOpen = null;
785-
const title = window.prompt(this.t('session.rename.prompt'), s.title || '');
786-
if (title != null && title.trim()) this.post('/api/session-rename', { id: s.id, title: title.trim() });
812+
this.askPrompt({
813+
title: this.t('session.rename.prompt'),
814+
value: s.title || '',
815+
onOk: (title) => { if (title) this.post('/api/session-rename', { id: s.id, title }); },
816+
});
787817
},
788818
deleteSession(s) {
789819
this.menuOpen = null;
790-
if (window.confirm(this.t('session.delete.confirm'))) this.post('/api/session-delete', { id: s.id });
820+
if (s.id === 'default') { this.showToast(this.t('session.delete.default_hint')); return; } // 默认会话不可删,即时反馈(issue #141)
821+
// 用 app 内自定义确认弹窗,不用浏览器原生 confirm。
822+
this.askConfirm({
823+
text: this.t('session.delete.confirm'),
824+
danger: true,
825+
onYes: () => this.post('/api/session-delete', { id: s.id }),
826+
});
791827
},
792828
setLang(lang) { this.post('/api/lang', { lang }); },
793829
setModel(role) { this.post('/api/model', { role }); },

web/static/index.html

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
<button class="session-menu-btn" @click.stop="toggleMenu(s.id)"></button>
3737
<div class="session-menu" v-if="menuOpen === s.id" @click.stop>
3838
<button @click="renameSession(s)">{{ t('session.rename') }}</button>
39-
<button class="danger" @click="deleteSession(s)">{{ t('session.delete') }}</button>
39+
<!-- 默认会话(rootDir 老会话)不可删:按钮置灰 + 悬停提示,但仍可点 —— 点了弹 toast 说明,
40+
绝不用 disabled(禁用按钮不触发 click,会变成"点了没反应",issue #141)。 -->
41+
<button class="danger" :class="{ muted: s.id === 'default' }"
42+
:title="s.id === 'default' ? t('session.delete.default_hint') : ''"
43+
@click="deleteSession(s)">{{ t('session.delete') }}</button>
4044
</div>
4145
</div>
4246
</div>
@@ -247,6 +251,30 @@
247251
</div>
248252
</div>
249253

254+
<!-- 通用确认弹层(替代浏览器原生 confirm,issue #141) -->
255+
<div v-if="confirmBox" class="review-mask" @click.self="confirmNo()">
256+
<div class="review-box confirm-box">
257+
<div class="confirm-text">{{ confirmBox.text }}</div>
258+
<div class="review-actions">
259+
<button class="cancel" @click="confirmNo()">{{ t('confirm.cancel') }}</button>
260+
<button :class="confirmBox.danger ? 'reject' : 'approve'" @click="confirmYes()">{{ t('confirm.ok') }}</button>
261+
</div>
262+
</div>
263+
</div>
264+
265+
<!-- 通用输入弹层(替代浏览器原生 prompt) -->
266+
<div v-if="promptBox" class="review-mask" @click.self="promptCancel()">
267+
<div class="review-box confirm-box">
268+
<div class="confirm-text">{{ promptBox.title }}</div>
269+
<input class="prompt-input" ref="promptInput" v-model="promptBox.value"
270+
:placeholder="promptBox.placeholder" @keyup.enter="promptOk()" @keyup.esc="promptCancel()">
271+
<div class="review-actions">
272+
<button class="cancel" @click="promptCancel()">{{ t('confirm.cancel') }}</button>
273+
<button class="approve" @click="promptOk()">{{ t('confirm.ok') }}</button>
274+
</div>
275+
</div>
276+
</div>
277+
250278
<!-- MCP 管理弹层 -->
251279
<div v-if="mcpShow" class="review-mask" @click.self="mcpShow=false">
252280
<div class="review-box mgr-box">

web/static/styles.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ body {
216216
.review-actions button { border: none; border-radius: 8px; padding: 8px 22px; font-weight: 600; cursor: pointer; }
217217
.review-actions .approve { background: var(--green); color: #ffffff; }
218218
.review-actions .reject { background: var(--red); color: #ffffff; }
219+
.review-actions .cancel { background: var(--bg2); color: var(--fg); border: 1px solid var(--border); }
220+
.review-actions .cancel:hover { border-color: var(--accent2); }
221+
222+
/* 通用确认 / 输入弹窗(替代浏览器原生 confirm / prompt) */
223+
.confirm-box { width: min(420px, 90vw); }
224+
.confirm-text { font-size: 14px; line-height: 1.6; color: var(--fg); }
225+
.prompt-input {
226+
width: 100%; box-sizing: border-box; margin-top: 12px; padding: 8px 10px;
227+
border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--fg);
228+
font: inherit; font-size: 14px;
229+
}
230+
.prompt-input:focus { outline: none; border-color: var(--accent); }
219231

220232
/* AskUser 选择题:内联进对话流的卡片(不再浮层弹窗,issue #134) */
221233
.chat-ask {
@@ -339,6 +351,8 @@ body {
339351
}
340352
.session-menu button:hover { background: var(--bg2); }
341353
.session-menu button.danger { color: var(--red); }
354+
/* 默认会话的删除按钮:置灰提示"不可删",但仍可点(点了弹 toast 说明,issue #141) */
355+
.session-menu button.danger.muted { opacity: .45; }
342356

343357
/* ── 输入框上方的控制条:路由 / 权限 / 沙箱 / 工作模式,每项一颗胶囊 ── */
344358
.controls-bar {

0 commit comments

Comments
 (0)