tavern-harness 0.1.20 → 0.1.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.
- package/CHANGELOG.md +42 -0
- package/package.json +1 -1
- package/src/App.tsx +44 -2
- package/src/components/SettingsView.tsx +31 -10
- package/src/components/Sidebar.tsx +20 -3
- package/src/components/shared.tsx +2 -0
- package/src/core/i18n.ts +18 -0
- package/src/generated/changelog.json +61 -1
- package/src/theme/global.css +64 -4
- package/src/theme/views.css +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,46 @@
|
|
|
3
3
|
> 本文件由 `scripts/changelog.mjs` 从 `changelog/v*.md` 生成,请勿手动编辑。
|
|
4
4
|
> 版本对比链接见文末;源文件位于仓库 `changelog/` 目录。
|
|
5
5
|
|
|
6
|
+
## [0.1.22] - 2026-10-26
|
|
7
|
+
|
|
8
|
+
### 中文
|
|
9
|
+
|
|
10
|
+
#### 改进
|
|
11
|
+
|
|
12
|
+
- 生成参数默认锁定:「设置 → 生成参数」里的滑杆和下拉框要先点「解锁」才能调整,离开设置页后会自动重新锁定,避免滑动页面时误改温度、上下文压缩等参数
|
|
13
|
+
- 「重置」按钮在锁定状态下不可点击,解锁后才能恢复默认值
|
|
14
|
+
- 会话列表的标题和副标题只在内容确实溢出时才做末尾渐隐,短标题不再被淡边影响
|
|
15
|
+
- 移动端顶部导航按钮之间的间距略微加大,点击时更不容易误触相邻按钮
|
|
16
|
+
|
|
17
|
+
### English
|
|
18
|
+
|
|
19
|
+
#### Changed
|
|
20
|
+
|
|
21
|
+
- Generation parameters are now locked by default: the sliders and dropdowns under Settings → Generation parameters stay read-only until you tap "Unlock", and they lock again when you leave the settings page, so scrolling can no longer change temperature, context compression, and other values by accident
|
|
22
|
+
- The "Reset" button is disabled while parameters are locked and becomes available after unlocking
|
|
23
|
+
- Session list titles and subtitles only fade out at the end when the text actually overflows, so short titles are no longer dimmed by the mask
|
|
24
|
+
- Mobile top-bar navigation buttons have a little more spacing, reducing accidental taps on neighbours
|
|
25
|
+
|
|
26
|
+
## [0.1.21] - 2026-10-26
|
|
27
|
+
|
|
28
|
+
### 中文
|
|
29
|
+
|
|
30
|
+
#### 改进
|
|
31
|
+
|
|
32
|
+
- 移动端会话头部的操作按钮收进「更多操作」菜单(工作区文件、编辑对话、重置对话、导出游玩记录、删除对话),头部不再拥挤;主题切换按钮仍然直接显示
|
|
33
|
+
- 移动端会话标题可用的横向空间明显变大,长标题能显示更多内容
|
|
34
|
+
- 会话列表中的标题和副标题过长时改为末尾渐隐,不再用省略号生硬截断
|
|
35
|
+
- 会话头部的上下文用量标签字号略微收小,和标题搭配更协调
|
|
36
|
+
|
|
37
|
+
### English
|
|
38
|
+
|
|
39
|
+
#### Changed
|
|
40
|
+
|
|
41
|
+
- On mobile, session header actions now collapse into a "More actions" menu (workspace files, edit, reset, export gameplay, delete), so the header is no longer cramped; the theme toggle stays one tap away
|
|
42
|
+
- Mobile session titles get noticeably more horizontal room, so long titles reveal more of their text
|
|
43
|
+
- Long session list titles and subtitles now fade out at the end instead of being cut off with an ellipsis
|
|
44
|
+
- The context usage badge in the session header uses a slightly smaller font so it sits better next to the title
|
|
45
|
+
|
|
6
46
|
## [0.1.20] - 2026-10-26
|
|
7
47
|
|
|
8
48
|
### 中文
|
|
@@ -588,6 +628,8 @@
|
|
|
588
628
|
- Vite + React + Zustand + Dexie stack with browser-side local persistence
|
|
589
629
|
|
|
590
630
|
<!-- 链接定义 -->
|
|
631
|
+
[0.1.22]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.21...v0.1.22
|
|
632
|
+
[0.1.21]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.20...v0.1.21
|
|
591
633
|
[0.1.20]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.19...v0.1.20
|
|
592
634
|
[0.1.19]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.18...v0.1.19
|
|
593
635
|
[0.1.18]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.17...v0.1.18
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -327,6 +327,8 @@ function SessionHeader({
|
|
|
327
327
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
328
328
|
const [exportOpen, setExportOpen] = useState(false);
|
|
329
329
|
const [showWorkspace, setShowWorkspace] = useState(false);
|
|
330
|
+
const [showMobileActions, setShowMobileActions] = useState(false);
|
|
331
|
+
const mobileActionsRef = useRef<HTMLDivElement>(null);
|
|
330
332
|
|
|
331
333
|
const messages = useStore((s) => s.messages[session.id!] ?? []);
|
|
332
334
|
const contextTokens = useMemo(() => {
|
|
@@ -350,6 +352,17 @@ function SessionHeader({
|
|
|
350
352
|
|| (themeMode === 'system' && document.documentElement.dataset.theme === 'dark');
|
|
351
353
|
const toggleTheme = () => void setSettings({ themeMode: isDarkTheme ? 'light' : 'dark' });
|
|
352
354
|
|
|
355
|
+
useEffect(() => {
|
|
356
|
+
if (!showMobileActions) return;
|
|
357
|
+
const closeOnOutsidePointerDown = (event: PointerEvent) => {
|
|
358
|
+
if (!mobileActionsRef.current?.contains(event.target as Node)) {
|
|
359
|
+
setShowMobileActions(false);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
document.addEventListener('pointerdown', closeOnOutsidePointerDown);
|
|
363
|
+
return () => document.removeEventListener('pointerdown', closeOnOutsidePointerDown);
|
|
364
|
+
}, [showMobileActions]);
|
|
365
|
+
|
|
353
366
|
return (
|
|
354
367
|
<div className="chat-header">
|
|
355
368
|
<SessionVisual mode={session.mode} npcName={npcRef?.name ?? undefined} hue={npcRef?.avatarColorOrdinal ?? 0} imageUrl={npcRef?.avatarDataUrl} members={session.mode === 'GROUP' ? groupMemberAvatars : undefined} size="lg" />
|
|
@@ -361,7 +374,7 @@ function SessionHeader({
|
|
|
361
374
|
</div>
|
|
362
375
|
)}
|
|
363
376
|
</div>
|
|
364
|
-
<div className="chat-actions">
|
|
377
|
+
<div className="chat-actions" ref={mobileActionsRef}>
|
|
365
378
|
<button className="btn-ghost icon-tooltip" title={t(usesPublicWorkspace ? 'header.workspaceRootHint' : 'header.workspaceHint')} onClick={() => setShowWorkspace(true)}>
|
|
366
379
|
<Icon name="folder" size={17} />
|
|
367
380
|
</button>
|
|
@@ -378,7 +391,17 @@ function SessionHeader({
|
|
|
378
391
|
<Icon name="trash" size={17} />
|
|
379
392
|
</button>
|
|
380
393
|
<button
|
|
381
|
-
className="btn-ghost icon-tooltip"
|
|
394
|
+
className="btn-ghost icon-tooltip chat-actions-more"
|
|
395
|
+
type="button"
|
|
396
|
+
title={t('header.moreActions')}
|
|
397
|
+
aria-label={t('header.moreActions')}
|
|
398
|
+
aria-expanded={showMobileActions}
|
|
399
|
+
onClick={() => setShowMobileActions(!showMobileActions)}
|
|
400
|
+
>
|
|
401
|
+
<Icon name="more" size={18} />
|
|
402
|
+
</button>
|
|
403
|
+
<button
|
|
404
|
+
className="btn-ghost icon-tooltip chat-theme-toggle"
|
|
382
405
|
type="button"
|
|
383
406
|
title={t(isDarkTheme ? 'header.switchToLightTip' : 'header.switchToDarkTip')}
|
|
384
407
|
aria-label={t(isDarkTheme ? 'header.switchToLightTip' : 'header.switchToDarkTip')}
|
|
@@ -386,6 +409,25 @@ function SessionHeader({
|
|
|
386
409
|
>
|
|
387
410
|
<Icon name={isDarkTheme ? 'sun' : 'moon'} size={17} />
|
|
388
411
|
</button>
|
|
412
|
+
{showMobileActions && (
|
|
413
|
+
<div className="chat-actions-menu card">
|
|
414
|
+
<button onClick={() => { setShowWorkspace(true); setShowMobileActions(false); }}>
|
|
415
|
+
<Icon name="folder" size={15} /> {t('header.workspaceAction')}
|
|
416
|
+
</button>
|
|
417
|
+
<button onClick={() => { setShowEdit(true); setShowMobileActions(false); }}>
|
|
418
|
+
<Icon name="pencil" size={15} /> {t('nav.editSession')}
|
|
419
|
+
</button>
|
|
420
|
+
<button onClick={() => { setConfirmReset(true); setShowMobileActions(false); }}>
|
|
421
|
+
<Icon name="refresh" size={15} /> {t('header.resetSessionTip')}
|
|
422
|
+
</button>
|
|
423
|
+
<button onClick={() => { setExportOpen(true); setShowMobileActions(false); }}>
|
|
424
|
+
<Icon name="share" size={15} /> {t('nav.exportGameplay')}
|
|
425
|
+
</button>
|
|
426
|
+
<button className="danger" onClick={() => { setConfirmDelete(true); setShowMobileActions(false); }}>
|
|
427
|
+
<Icon name="trash" size={15} /> {t('header.deleteTip')}
|
|
428
|
+
</button>
|
|
429
|
+
</div>
|
|
430
|
+
)}
|
|
389
431
|
</div>
|
|
390
432
|
|
|
391
433
|
{showEdit && <NewSessionMenu editingSession={session} onClose={() => setShowEdit(false)} />}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo, useState } from 'react';
|
|
1
|
+
import { memo, useEffect, useState } from 'react';
|
|
2
2
|
import { useStore } from '../store/store';
|
|
3
3
|
import { db, DEFAULT_GENERATION_SETTINGS } from '../db/database';
|
|
4
4
|
import { type ThemeMode } from '../theme/theme';
|
|
@@ -24,6 +24,8 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
24
24
|
const [showResetConfirm, setShowResetConfirm] = useState(false);
|
|
25
25
|
const [resetting, setResetting] = useState(false);
|
|
26
26
|
const [showVersionHistory, setShowVersionHistory] = useState(false);
|
|
27
|
+
const [generationParamsLocked, setGenerationParamsLocked] = useState(true);
|
|
28
|
+
const activeView = useStore((s) => s.activeView);
|
|
27
29
|
const t = useT();
|
|
28
30
|
const hasUnseenVersion = useHasUnseenVersion();
|
|
29
31
|
|
|
@@ -38,6 +40,7 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
38
40
|
max={max}
|
|
39
41
|
step={step}
|
|
40
42
|
value={settings[key] as number}
|
|
43
|
+
disabled={generationParamsLocked}
|
|
41
44
|
onChange={(e) => setSettings({ [key]: parseFloat(e.target.value) })}
|
|
42
45
|
/>
|
|
43
46
|
<span className="range-val">{fmt ? fmt(settings[key] as number) : (settings[key] as number)}</span>
|
|
@@ -52,6 +55,11 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
52
55
|
await setSettings({ ...generationDefaults });
|
|
53
56
|
addToast(t('toast.genParamsReset'));
|
|
54
57
|
};
|
|
58
|
+
const toggleGenerationParamsLock = () => setGenerationParamsLocked((locked) => !locked);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (activeView !== 'settings') setGenerationParamsLocked(true);
|
|
62
|
+
}, [activeView]);
|
|
55
63
|
|
|
56
64
|
return (
|
|
57
65
|
<div className="view-page">
|
|
@@ -104,15 +112,26 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
104
112
|
<div className="set-section">
|
|
105
113
|
<div className="settings-section-head">
|
|
106
114
|
<h3>{t('settings.genParams')}</h3>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
<div className="settings-gen-actions">
|
|
116
|
+
<button
|
|
117
|
+
className="btn btn-sm settings-gen-lock"
|
|
118
|
+
title={t(generationParamsLocked ? 'settings.genParamsUnlock' : 'settings.genParamsLock')}
|
|
119
|
+
aria-label={t(generationParamsLocked ? 'settings.genParamsUnlock' : 'settings.genParamsLock')}
|
|
120
|
+
aria-pressed={generationParamsLocked}
|
|
121
|
+
onClick={toggleGenerationParamsLock}
|
|
122
|
+
>
|
|
123
|
+
<Icon name={generationParamsLocked ? 'lock' : 'unlock'} size={12} /> {t(generationParamsLocked ? 'settings.genParamsLocked' : 'settings.genParamsUnlocked')}
|
|
124
|
+
</button>
|
|
125
|
+
<button
|
|
126
|
+
className="btn btn-sm settings-gen-reset"
|
|
127
|
+
title={t('settings.genParamsResetTitle')}
|
|
128
|
+
aria-label={t('settings.genParamsResetTitle')}
|
|
129
|
+
disabled={generationParamsLocked || isGenerationDefault}
|
|
130
|
+
onClick={() => void resetGenerationParams()}
|
|
131
|
+
>
|
|
132
|
+
<Icon name="refresh" size={12} /> {t('settings.genParamsReset')}
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
116
135
|
</div>
|
|
117
136
|
<table className="table param-table">
|
|
118
137
|
<tbody>
|
|
@@ -178,6 +197,7 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
178
197
|
max="100"
|
|
179
198
|
step="1"
|
|
180
199
|
value={settings.contextCompressionThreshold === 'off' ? 0 : settings.contextCompressionThreshold}
|
|
200
|
+
disabled={generationParamsLocked}
|
|
181
201
|
onChange={(e) => setSettings({ contextCompressionThreshold: Number(e.target.value) || 'off' })}
|
|
182
202
|
/>
|
|
183
203
|
<span className="range-val">
|
|
@@ -195,6 +215,7 @@ export const SettingsView = memo(function SettingsView() {
|
|
|
195
215
|
<select
|
|
196
216
|
className="select"
|
|
197
217
|
value={settings.reasoningEffort}
|
|
218
|
+
disabled={generationParamsLocked}
|
|
198
219
|
onChange={(e) => setSettings({ reasoningEffort: e.target.value as ReasoningEffort })}
|
|
199
220
|
>
|
|
200
221
|
{['auto', 'off', 'low', 'medium', 'xhigh'].map((r) => <option key={r} value={r}>{r}</option>)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState, type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
3
|
import { QRCodeSVG } from 'qrcode.react';
|
|
4
4
|
import { useStore } from '../store/store';
|
|
@@ -19,6 +19,23 @@ const SHAREHOLDER_CLICK_COUNT = 5;
|
|
|
19
19
|
const SHAREHOLDER_CLICK_WINDOW_MS = 5_000;
|
|
20
20
|
const SIDEBAR_MAX = 520;
|
|
21
21
|
|
|
22
|
+
function FadeOverflowText({ className, children, ...props }: ComponentPropsWithoutRef<'span'> & { children: ReactNode }) {
|
|
23
|
+
const ref = useRef<HTMLSpanElement>(null);
|
|
24
|
+
const [overflowing, setOverflowing] = useState(false);
|
|
25
|
+
|
|
26
|
+
useLayoutEffect(() => {
|
|
27
|
+
const element = ref.current;
|
|
28
|
+
if (!element) return;
|
|
29
|
+
const updateOverflow = () => setOverflowing(element.scrollWidth > element.clientWidth);
|
|
30
|
+
updateOverflow();
|
|
31
|
+
const observer = new ResizeObserver(updateOverflow);
|
|
32
|
+
observer.observe(element);
|
|
33
|
+
return () => observer.disconnect();
|
|
34
|
+
}, [children]);
|
|
35
|
+
|
|
36
|
+
return <span ref={ref} className={`${className ?? ''}${overflowing ? ' has-overflow' : ''}`} {...props}>{children}</span>;
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
/** 触发 App 级全局游戏导出弹窗(会话右键菜单共用) */
|
|
23
40
|
function openGlobalGameplayExport(sessionId: number, includeHistory: boolean) {
|
|
24
41
|
window.dispatchEvent(
|
|
@@ -409,10 +426,10 @@ export function Sidebar() {
|
|
|
409
426
|
{!!(s as { pinned?: unknown }).pinned && (
|
|
410
427
|
<span className="spinned" title={t('nav.pinSession')}><Icon name="pin" size={11} /></span>
|
|
411
428
|
)}
|
|
412
|
-
<
|
|
429
|
+
<FadeOverflowText className="stitle-text">{s.title}</FadeOverflowText>
|
|
413
430
|
{hasActivity && <span className="session-activity-dot" aria-label={t('nav.sessionActive')} />}
|
|
414
431
|
</div>
|
|
415
|
-
<
|
|
432
|
+
<FadeOverflowText className="sprev">{s.lastMessage || t('nav.newConversation')}</FadeOverflowText>
|
|
416
433
|
</div>
|
|
417
434
|
</button>
|
|
418
435
|
);
|
|
@@ -409,6 +409,8 @@ const ICONS: Record<string, React.ReactNode> = {
|
|
|
409
409
|
upload: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="m17 8-5-5-5 5M12 3v12" /></>,
|
|
410
410
|
import: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="M17 14l-5 5-5-5M12 19V7" /></>,
|
|
411
411
|
pin: <><path d="M9 4h6M12 4v3l4 3v3h-8v-3l4-3" /><path d="M12 13v7" /></>,
|
|
412
|
+
lock: <><rect x="5" y="10" width="14" height="11" rx="2" /><path d="M8 10V7a4 4 0 0 1 8 0v3" /></>,
|
|
413
|
+
unlock: <><rect x="5" y="10" width="14" height="11" rx="2" /><path d="M8 10V7a4 4 0 0 1 7.4-1.9" /></>,
|
|
412
414
|
'git-branch': <><path d="M6 3v12" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" /></>,
|
|
413
415
|
'zoom-in': <><circle cx="11" cy="11" r="7" /><path d="m21 21-4.35-4.35M11 8v6M8 11h6" /></>,
|
|
414
416
|
'zoom-out': <><circle cx="11" cy="11" r="7" /><path d="m21 21-4.35-4.35M8 11h6" /></>,
|
package/src/core/i18n.ts
CHANGED
|
@@ -81,6 +81,8 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
81
81
|
'header.modeStandard': '标准对话',
|
|
82
82
|
'header.modeNpc': '角色对话',
|
|
83
83
|
'header.modeGroup': '群聊',
|
|
84
|
+
'header.moreActions': '更多操作',
|
|
85
|
+
'header.workspaceAction': '工作区文件',
|
|
84
86
|
'header.editSessionTip': '编辑对话设置',
|
|
85
87
|
'header.resetSessionTip': '重置对话',
|
|
86
88
|
'header.resetSessionTitle': '重置对话',
|
|
@@ -200,6 +202,10 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
200
202
|
'settings.yoloMode': 'YOLO 模式',
|
|
201
203
|
'settings.yoloModeDesc': '自动批准工具修改和 Shell 执行,无需逐次确认。仅在信任角色和技能时开启。',
|
|
202
204
|
'settings.genParams': '🎛 生成参数',
|
|
205
|
+
'settings.genParamsLocked': '已锁定',
|
|
206
|
+
'settings.genParamsUnlocked': '已解锁',
|
|
207
|
+
'settings.genParamsLock': '锁定生成参数',
|
|
208
|
+
'settings.genParamsUnlock': '解锁生成参数',
|
|
203
209
|
'settings.genParamsReset': '重置',
|
|
204
210
|
'settings.genParamsResetTitle': '将生成参数恢复为默认值',
|
|
205
211
|
'settings.tempDesc': '创造性与随机性',
|
|
@@ -605,6 +611,8 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
605
611
|
'header.modeStandard': '標準對話',
|
|
606
612
|
'header.modeNpc': '角色對話',
|
|
607
613
|
'header.modeGroup': '群聊',
|
|
614
|
+
'header.moreActions': '更多操作',
|
|
615
|
+
'header.workspaceAction': '工作區檔案',
|
|
608
616
|
'header.editSessionTip': '編輯對話設定',
|
|
609
617
|
'header.resetSessionTip': '重置對話',
|
|
610
618
|
'header.resetSessionTitle': '重置對話',
|
|
@@ -721,6 +729,10 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
721
729
|
'settings.yoloMode': 'YOLO 模式',
|
|
722
730
|
'settings.yoloModeDesc': '自動批准工具修改和 Shell 執行,無需逐次確認。僅在信任角色和技能時開啟。',
|
|
723
731
|
'settings.genParams': '🎛 產生參數',
|
|
732
|
+
'settings.genParamsLocked': '已鎖定',
|
|
733
|
+
'settings.genParamsUnlocked': '已解鎖',
|
|
734
|
+
'settings.genParamsLock': '鎖定產生參數',
|
|
735
|
+
'settings.genParamsUnlock': '解鎖產生參數',
|
|
724
736
|
'settings.genParamsReset': '重設',
|
|
725
737
|
'settings.genParamsResetTitle': '將產生參數恢復為預設值',
|
|
726
738
|
'settings.tempDesc': '創造性與隨機性',
|
|
@@ -1117,6 +1129,8 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
1117
1129
|
'header.modeStandard': 'Standard chat',
|
|
1118
1130
|
'header.modeNpc': 'Character chat',
|
|
1119
1131
|
'header.modeGroup': 'Group chat',
|
|
1132
|
+
'header.moreActions': 'More actions',
|
|
1133
|
+
'header.workspaceAction': 'Workspace files',
|
|
1120
1134
|
'header.editSessionTip': 'Edit conversation settings',
|
|
1121
1135
|
'header.resetSessionTip': 'Reset conversation',
|
|
1122
1136
|
'header.resetSessionTitle': 'Reset conversation',
|
|
@@ -1233,6 +1247,10 @@ const MESSAGES: Record<LangKey, Record<string, string>> = {
|
|
|
1233
1247
|
'settings.yoloMode': 'YOLO mode',
|
|
1234
1248
|
'settings.yoloModeDesc': 'Automatically approve tool changes and Shell commands without asking each time. Only enable for trusted characters and skills.',
|
|
1235
1249
|
'settings.genParams': '🎛 Generation parameters',
|
|
1250
|
+
'settings.genParamsLocked': 'Locked',
|
|
1251
|
+
'settings.genParamsUnlocked': 'Unlocked',
|
|
1252
|
+
'settings.genParamsLock': 'Lock generation parameters',
|
|
1253
|
+
'settings.genParamsUnlock': 'Unlock generation parameters',
|
|
1236
1254
|
'settings.genParamsReset': 'Reset',
|
|
1237
1255
|
'settings.genParamsResetTitle': 'Restore generation parameters to their default values',
|
|
1238
1256
|
'settings.tempDesc': 'Creativity & randomness',
|
|
@@ -1,7 +1,67 @@
|
|
|
1
1
|
{
|
|
2
|
-
"latest": "0.1.
|
|
2
|
+
"latest": "0.1.22",
|
|
3
3
|
"repo": "https://github.com/Rannichan/Tavern-Harness",
|
|
4
4
|
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.1.22",
|
|
7
|
+
"date": "2026-10-26",
|
|
8
|
+
"breaking": false,
|
|
9
|
+
"compare": "v0.1.21...v0.1.22",
|
|
10
|
+
"groups": {
|
|
11
|
+
"zh-CN": [
|
|
12
|
+
{
|
|
13
|
+
"kind": "changed",
|
|
14
|
+
"items": [
|
|
15
|
+
"生成参数默认锁定:「设置 → 生成参数」里的滑杆和下拉框要先点「解锁」才能调整,离开设置页后会自动重新锁定,避免滑动页面时误改温度、上下文压缩等参数",
|
|
16
|
+
"「重置」按钮在锁定状态下不可点击,解锁后才能恢复默认值",
|
|
17
|
+
"会话列表的标题和副标题只在内容确实溢出时才做末尾渐隐,短标题不再被淡边影响",
|
|
18
|
+
"移动端顶部导航按钮之间的间距略微加大,点击时更不容易误触相邻按钮"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"en": [
|
|
23
|
+
{
|
|
24
|
+
"kind": "changed",
|
|
25
|
+
"items": [
|
|
26
|
+
"Generation parameters are now locked by default: the sliders and dropdowns under Settings → Generation parameters stay read-only until you tap \"Unlock\", and they lock again when you leave the settings page, so scrolling can no longer change temperature, context compression, and other values by accident",
|
|
27
|
+
"The \"Reset\" button is disabled while parameters are locked and becomes available after unlocking",
|
|
28
|
+
"Session list titles and subtitles only fade out at the end when the text actually overflows, so short titles are no longer dimmed by the mask",
|
|
29
|
+
"Mobile top-bar navigation buttons have a little more spacing, reducing accidental taps on neighbours"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"version": "0.1.21",
|
|
37
|
+
"date": "2026-10-26",
|
|
38
|
+
"breaking": false,
|
|
39
|
+
"compare": "v0.1.20...v0.1.21",
|
|
40
|
+
"groups": {
|
|
41
|
+
"zh-CN": [
|
|
42
|
+
{
|
|
43
|
+
"kind": "changed",
|
|
44
|
+
"items": [
|
|
45
|
+
"移动端会话头部的操作按钮收进「更多操作」菜单(工作区文件、编辑对话、重置对话、导出游玩记录、删除对话),头部不再拥挤;主题切换按钮仍然直接显示",
|
|
46
|
+
"移动端会话标题可用的横向空间明显变大,长标题能显示更多内容",
|
|
47
|
+
"会话列表中的标题和副标题过长时改为末尾渐隐,不再用省略号生硬截断",
|
|
48
|
+
"会话头部的上下文用量标签字号略微收小,和标题搭配更协调"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"en": [
|
|
53
|
+
{
|
|
54
|
+
"kind": "changed",
|
|
55
|
+
"items": [
|
|
56
|
+
"On mobile, session header actions now collapse into a \"More actions\" menu (workspace files, edit, reset, export gameplay, delete), so the header is no longer cramped; the theme toggle stays one tap away",
|
|
57
|
+
"Mobile session titles get noticeably more horizontal room, so long titles reveal more of their text",
|
|
58
|
+
"Long session list titles and subtitles now fade out at the end instead of being cut off with an ellipsis",
|
|
59
|
+
"The context usage badge in the session header uses a slightly smaller font so it sits better next to the title"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
5
65
|
{
|
|
6
66
|
"version": "0.1.20",
|
|
7
67
|
"date": "2026-10-26",
|
package/src/theme/global.css
CHANGED
|
@@ -573,10 +573,14 @@ body.resizing .sidebar-resizer {
|
|
|
573
573
|
}
|
|
574
574
|
.session-item .stitle-text {
|
|
575
575
|
overflow: hidden;
|
|
576
|
-
text-overflow: ellipsis;
|
|
577
576
|
white-space: nowrap;
|
|
578
577
|
min-width: 0;
|
|
579
578
|
}
|
|
579
|
+
.session-item .stitle-text.has-overflow,
|
|
580
|
+
.session-item .sprev.has-overflow {
|
|
581
|
+
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent);
|
|
582
|
+
mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent);
|
|
583
|
+
}
|
|
580
584
|
.session-item .spinned {
|
|
581
585
|
display: inline-flex;
|
|
582
586
|
align-items: center;
|
|
@@ -611,11 +615,11 @@ body.resizing .sidebar-resizer {
|
|
|
611
615
|
}
|
|
612
616
|
}
|
|
613
617
|
.session-item .sprev {
|
|
618
|
+
display: block;
|
|
614
619
|
font-size: 11.5px;
|
|
615
620
|
color: var(--text-faint);
|
|
616
621
|
white-space: nowrap;
|
|
617
622
|
overflow: hidden;
|
|
618
|
-
text-overflow: ellipsis;
|
|
619
623
|
}
|
|
620
624
|
/* ---------- 会话区域(头部 + 滚动列表) ---------- */
|
|
621
625
|
.session-section {
|
|
@@ -1072,6 +1076,7 @@ body.resizing .sidebar-resizer {
|
|
|
1072
1076
|
}
|
|
1073
1077
|
.chat-header .tsub-mode {
|
|
1074
1078
|
flex-shrink: 0;
|
|
1079
|
+
font-size: 12px;
|
|
1075
1080
|
font-weight: 700;
|
|
1076
1081
|
color: var(--primary);
|
|
1077
1082
|
}
|
|
@@ -1111,6 +1116,10 @@ body.resizing .sidebar-resizer {
|
|
|
1111
1116
|
color: var(--danger);
|
|
1112
1117
|
background: var(--danger-soft);
|
|
1113
1118
|
}
|
|
1119
|
+
.chat-actions .chat-actions-more,
|
|
1120
|
+
.chat-actions-menu {
|
|
1121
|
+
display: none;
|
|
1122
|
+
}
|
|
1114
1123
|
|
|
1115
1124
|
/* ---------- 卡片/仪表盘 ---------- */
|
|
1116
1125
|
.dash-scroll {
|
|
@@ -1673,7 +1682,7 @@ hr.sep {
|
|
|
1673
1682
|
display: flex;
|
|
1674
1683
|
flex: 1;
|
|
1675
1684
|
min-width: 0;
|
|
1676
|
-
gap:
|
|
1685
|
+
gap: 8px;
|
|
1677
1686
|
margin: -12px -10px;
|
|
1678
1687
|
padding: 12px 10px;
|
|
1679
1688
|
overflow-x: auto;
|
|
@@ -1799,7 +1808,7 @@ hr.sep {
|
|
|
1799
1808
|
}
|
|
1800
1809
|
|
|
1801
1810
|
.chat-header .ttitle {
|
|
1802
|
-
max-width: min(
|
|
1811
|
+
max-width: min(52vw, 260px);
|
|
1803
1812
|
font-size: 15px;
|
|
1804
1813
|
}
|
|
1805
1814
|
|
|
@@ -1808,16 +1817,67 @@ hr.sep {
|
|
|
1808
1817
|
}
|
|
1809
1818
|
|
|
1810
1819
|
.chat-actions {
|
|
1820
|
+
position: relative;
|
|
1811
1821
|
flex: 0 0 auto;
|
|
1812
1822
|
gap: 0;
|
|
1813
1823
|
}
|
|
1814
1824
|
|
|
1825
|
+
.chat-actions .icon-tooltip:not(.chat-actions-more, .chat-theme-toggle) {
|
|
1826
|
+
display: none;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.chat-actions .chat-actions-more {
|
|
1830
|
+
display: inline-flex;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1815
1833
|
.chat-actions .icon-tooltip {
|
|
1816
1834
|
width: 34px;
|
|
1817
1835
|
height: 34px;
|
|
1818
1836
|
flex-shrink: 0;
|
|
1819
1837
|
}
|
|
1820
1838
|
|
|
1839
|
+
.chat-actions-menu {
|
|
1840
|
+
position: absolute;
|
|
1841
|
+
top: calc(100% + 6px);
|
|
1842
|
+
right: 0;
|
|
1843
|
+
z-index: 60;
|
|
1844
|
+
display: flex;
|
|
1845
|
+
flex-direction: column;
|
|
1846
|
+
min-width: 164px;
|
|
1847
|
+
padding: 6px;
|
|
1848
|
+
background: var(--surface);
|
|
1849
|
+
border: 1px solid var(--border);
|
|
1850
|
+
border-radius: 12px;
|
|
1851
|
+
box-shadow: var(--shadow-lg);
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
.chat-actions-menu button {
|
|
1855
|
+
display: flex;
|
|
1856
|
+
align-items: center;
|
|
1857
|
+
gap: 9px;
|
|
1858
|
+
width: 100%;
|
|
1859
|
+
padding: 8px 10px;
|
|
1860
|
+
border: none;
|
|
1861
|
+
border-radius: 8px;
|
|
1862
|
+
background: none;
|
|
1863
|
+
color: var(--text);
|
|
1864
|
+
font-size: 13px;
|
|
1865
|
+
font-weight: 600;
|
|
1866
|
+
text-align: left;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
.chat-actions-menu button:hover {
|
|
1870
|
+
background: var(--surface-2);
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
.chat-actions-menu button.danger {
|
|
1874
|
+
color: var(--danger);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
.chat-actions-menu button.danger:hover {
|
|
1878
|
+
background: var(--danger-soft);
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1821
1881
|
.chat-scroll {
|
|
1822
1882
|
padding-top: 14px;
|
|
1823
1883
|
}
|
package/src/theme/views.css
CHANGED
|
@@ -625,7 +625,13 @@
|
|
|
625
625
|
justify-content: space-between;
|
|
626
626
|
gap: 12px;
|
|
627
627
|
}
|
|
628
|
-
.settings-gen-
|
|
628
|
+
.settings-gen-actions {
|
|
629
|
+
display: flex;
|
|
630
|
+
flex-shrink: 0;
|
|
631
|
+
gap: 6px;
|
|
632
|
+
}
|
|
633
|
+
.settings-gen-reset,
|
|
634
|
+
.settings-gen-lock {
|
|
629
635
|
flex-shrink: 0;
|
|
630
636
|
}
|
|
631
637
|
.settings-reset-section {
|