tavern-harness 0.1.0
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/README.md +161 -0
- package/README.zh.md +123 -0
- package/bin/cli.js +48 -0
- package/index.html +35 -0
- package/package.json +50 -0
- package/public/favicon.png +0 -0
- package/public/tavern-harness-logo.png +0 -0
- package/sandbox-server.mjs +997 -0
- package/src/App.tsx +268 -0
- package/src/assets/tavern-handbook.md +39 -0
- package/src/components/AchievementModal.tsx +158 -0
- package/src/components/CharactersView.tsx +904 -0
- package/src/components/ChatView.tsx +1639 -0
- package/src/components/ConfirmationDialog.tsx +56 -0
- package/src/components/Dashboard.tsx +180 -0
- package/src/components/DeleteConfirmDialog.tsx +48 -0
- package/src/components/FileDisplayModal.tsx +556 -0
- package/src/components/GameplayDialogs.tsx +191 -0
- package/src/components/NewSessionMenu.tsx +362 -0
- package/src/components/SettingsView.tsx +517 -0
- package/src/components/Sidebar.tsx +392 -0
- package/src/components/StatsView.tsx +189 -0
- package/src/components/Toasts.tsx +20 -0
- package/src/components/WorkspaceFileManagerModal.tsx +216 -0
- package/src/components/shared.tsx +369 -0
- package/src/core/achievements.ts +85 -0
- package/src/core/fileDownload.ts +67 -0
- package/src/core/gameplay.ts +521 -0
- package/src/core/horizontalWheel.ts +93 -0
- package/src/core/i18n.ts +1403 -0
- package/src/core/linkedScroll.ts +236 -0
- package/src/core/markdown.ts +170 -0
- package/src/core/openai.ts +451 -0
- package/src/core/openingGreeting.ts +41 -0
- package/src/core/prompts.ts +254 -0
- package/src/core/proxy.ts +58 -0
- package/src/core/sillyTavernImporter.ts +317 -0
- package/src/core/stats.ts +91 -0
- package/src/core/strings.ts +19 -0
- package/src/core/toolDefinitions.ts +400 -0
- package/src/core/tools/builtinTools.ts +38 -0
- package/src/core/tools/generatedSkillExecutor.ts +706 -0
- package/src/core/tools/generatedWorkspace.ts +13 -0
- package/src/core/tools/toolExecutor.ts +718 -0
- package/src/core/turnLoop.ts +211 -0
- package/src/db/database.ts +647 -0
- package/src/main.tsx +23 -0
- package/src/store/store.ts +2419 -0
- package/src/theme/chat.css +1227 -0
- package/src/theme/global.css +1377 -0
- package/src/theme/theme.ts +143 -0
- package/src/theme/views.css +1430 -0
- package/src/types/models.ts +364 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +21 -0
- package/vite.config.ts +273 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export type ThemeMode = 'system' | 'light' | 'dark';
|
|
2
|
+
|
|
3
|
+
interface ThemePalette {
|
|
4
|
+
/** 深色模式主色(烛光发光色,浅亮) */
|
|
5
|
+
primary: string;
|
|
6
|
+
/** 浅色模式主色(在羊皮纸背景上有足够对比的深色变体) */
|
|
7
|
+
primaryLight: string;
|
|
8
|
+
primaryDim: string;
|
|
9
|
+
accentSoft: string;
|
|
10
|
+
/** 聊天栏气泡渐变端点 */
|
|
11
|
+
gradientFrom: string;
|
|
12
|
+
gradientTo: string;
|
|
13
|
+
glow: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 固定主题色:赛博琥珀(与 MyAgent-Android 温暖烛光一致) */
|
|
17
|
+
const AMBER_PALETTE: ThemePalette = {
|
|
18
|
+
primary: '#FFCB8B',
|
|
19
|
+
primaryLight: '#9A5B17',
|
|
20
|
+
primaryDim: '#C88A3F',
|
|
21
|
+
accentSoft: 'rgba(255,203,139,0.16)',
|
|
22
|
+
gradientFrom: '#d9a673',
|
|
23
|
+
gradientTo: '#d9a673',
|
|
24
|
+
glow: 'rgba(255,178,96,0.34)',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function isDarkMode(mode: ThemeMode): boolean {
|
|
28
|
+
if (mode === 'system') {
|
|
29
|
+
return typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
30
|
+
}
|
|
31
|
+
return mode === 'dark';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let themeWatcherInstalled = false;
|
|
35
|
+
|
|
36
|
+
/** 监听系统深浅色切换,模式为「跟随系统」时自动重新应用主题 */
|
|
37
|
+
export function watchSystemTheme(onChange: () => void): void {
|
|
38
|
+
if (themeWatcherInstalled || typeof window === 'undefined') return;
|
|
39
|
+
themeWatcherInstalled = true;
|
|
40
|
+
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
41
|
+
const listener = () => onChange();
|
|
42
|
+
if (typeof mq.addEventListener === 'function') {
|
|
43
|
+
mq.addEventListener('change', listener);
|
|
44
|
+
} else {
|
|
45
|
+
// 旧版 Safari 回退
|
|
46
|
+
(mq as unknown as { addListener: (fn: () => void) => void }).addListener(listener);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const THEME_CACHE_KEY = 'tav-theme-mode';
|
|
51
|
+
|
|
52
|
+
/** 同步写出主题模式缓存,供下次页面加载首帧直接取用(消除刷新闪动) */
|
|
53
|
+
export function cacheThemeMode(mode: ThemeMode): void {
|
|
54
|
+
try {
|
|
55
|
+
localStorage.setItem(THEME_CACHE_KEY, mode);
|
|
56
|
+
} catch {
|
|
57
|
+
// 隐私模式等环境下忽略
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 读取缓存的主题模式(可能为 null) */
|
|
62
|
+
function cachedThemeMode(): ThemeMode | null {
|
|
63
|
+
try {
|
|
64
|
+
const v = localStorage.getItem(THEME_CACHE_KEY);
|
|
65
|
+
return v === 'light' || v === 'dark' || v === 'system' ? v : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 应用主题 CSS 变量到 :root(固定使用琥珀色主题) */
|
|
72
|
+
export function applyTheme(mode: ThemeMode): void {
|
|
73
|
+
const dark = isDarkMode(mode);
|
|
74
|
+
const p = AMBER_PALETTE;
|
|
75
|
+
const root = document.documentElement;
|
|
76
|
+
const is = (light: string, darkV: string) => (dark ? darkV : light);
|
|
77
|
+
|
|
78
|
+
root.dataset.theme = dark ? 'dark' : 'light';
|
|
79
|
+
root.style.colorScheme = dark ? 'dark' : 'light';
|
|
80
|
+
|
|
81
|
+
const vars: Record<string, string> = {
|
|
82
|
+
// 主色:浅色模式用深色变体(羊皮纸底上对比足够),深色模式用烛光浅亮色
|
|
83
|
+
'--primary': is(p.primaryLight, p.primary),
|
|
84
|
+
'--primary-dim': is(p.primaryLight, p.primaryDim),
|
|
85
|
+
'--primary-soft': dark ? p.accentSoft : 'color-mix(in srgb, var(--primary) 12%, transparent)',
|
|
86
|
+
'--grad-from': is(p.primaryLight, p.gradientFrom),
|
|
87
|
+
'--grad-to': is(p.primaryLight, p.gradientTo),
|
|
88
|
+
'--glow': is('color-mix(in srgb, var(--primary) 28%, transparent)', p.glow),
|
|
89
|
+
|
|
90
|
+
'--bg': is('#F7F2E9', '#17120A'),
|
|
91
|
+
'--bg-deep': is('#efe8da', '#0f0b06'),
|
|
92
|
+
'--surface': is('#FDFBF5', '#211A10'),
|
|
93
|
+
'--surface-2': is('#efe8da', '#2B2215'),
|
|
94
|
+
'--surface-3': is('#e6ddca', '#362A1A'),
|
|
95
|
+
'--border': is('rgba(60,40,10,0.14)', 'rgba(255,214,160,0.13)'),
|
|
96
|
+
'--border-soft': is('rgba(60,40,10,0.08)', 'rgba(255,214,160,0.07)'),
|
|
97
|
+
|
|
98
|
+
'--text': is('#241c10', '#F2EADC'),
|
|
99
|
+
'--text-dim': is('#7a6a50', '#AA9C86'),
|
|
100
|
+
'--text-faint': is('#a3957c', '#6f6352'),
|
|
101
|
+
|
|
102
|
+
'--bubble-user': is('color-mix(in srgb, var(--primary) 11%, transparent)', 'color-mix(in srgb, var(--primary) 13%, transparent)'),
|
|
103
|
+
'--bubble-border-user': is('color-mix(in srgb, var(--primary) 24%, transparent)', 'color-mix(in srgb, var(--primary) 22%, transparent)'),
|
|
104
|
+
'--bubble-npc': is('#FDFBF5', '#241C12'),
|
|
105
|
+
'--bubble-border': is('rgba(60,40,10,0.12)', 'rgba(255,214,160,0.10)'),
|
|
106
|
+
|
|
107
|
+
'--code-bg': is('#ece4d2', '#282015'),
|
|
108
|
+
'--code-bg-inline': is('#e6dcc6', '#382C1B'),
|
|
109
|
+
'--tag-bg': is('color-mix(in srgb, var(--primary) 11%, transparent)', 'color-mix(in srgb, var(--primary) 13%, transparent)'),
|
|
110
|
+
'--tag-text': is('var(--primary)', 'color-mix(in srgb, var(--primary) 78%, #e8cfa0)'),
|
|
111
|
+
|
|
112
|
+
'--danger': is('#a3382c', '#ff9b8f'),
|
|
113
|
+
'--danger-soft': is('rgba(163,56,44,0.09)', 'rgba(255,155,143,0.12)'),
|
|
114
|
+
'--success': is('#2f6b45', '#92d9a8'),
|
|
115
|
+
'--success-soft': is('rgba(47,107,69,0.09)', 'rgba(146,217,168,0.12)'),
|
|
116
|
+
'--warn': is('#9a6416', '#ffc46b'),
|
|
117
|
+
'--warn-soft': is('rgba(154,100,22,0.10)', 'rgba(255,196,107,0.12)'),
|
|
118
|
+
|
|
119
|
+
'--shadow': is('0 1px 2px rgba(60,40,10,0.05), 0 4px 16px rgba(60,40,10,0.10)', '0 2px 10px rgba(0,0,0,0.35), 0 8px 30px rgba(0,0,0,0.45)'),
|
|
120
|
+
'--shadow-lg': is('0 2px 4px rgba(60,40,10,0.06), 0 16px 44px rgba(60,40,10,0.16)', '0 8px 24px rgba(0,0,0,0.5), 0 24px 70px rgba(0,0,0,0.65)'),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
Object.entries(vars).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
124
|
+
|
|
125
|
+
// 琥珀主题的暗色背景(暖木色)
|
|
126
|
+
if (dark) {
|
|
127
|
+
root.style.setProperty('--bg', '#20160E');
|
|
128
|
+
root.style.setProperty('--surface', '#211A10');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** 首帧同步应用主题(无缓存时按系统偏好),避免刷新出现主题闪动 */
|
|
133
|
+
export function applyThemeFromCache(): void {
|
|
134
|
+
const cached = cachedThemeMode();
|
|
135
|
+
const mode: ThemeMode =
|
|
136
|
+
cached ??
|
|
137
|
+
(typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
138
|
+
? 'dark'
|
|
139
|
+
: 'light');
|
|
140
|
+
applyTheme(mode);
|
|
141
|
+
// 背景在 body 上,确保首帧背景色一致
|
|
142
|
+
document.documentElement.style.backgroundColor = isDarkMode(mode) ? '#20160E' : '#F7F2E9';
|
|
143
|
+
}
|