dsh-remote-plugin 0.5.7 → 0.5.8
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.en.md +1 -0
- package/README.md +1 -0
- package/apk/dsh-remote.apk +0 -0
- package/package.json +1 -1
- package/public/app.js +373 -41
- package/public/desktop/desktop.css +207 -0
- package/public/desktop/desktop.html +247 -0
- package/public/desktop/desktop.js +917 -0
- package/public/desktop/i18n.js +53 -0
- package/public/index.html +53 -0
- package/public/styles.css +28 -351
- package/public/theme-vars.css +351 -0
- package/public/update.json +3 -3
- package/public/version.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/* DSH Remote 桌面端专用样式 · 零依赖 · 只引用 --dsr-* 皮肤变量 */
|
|
2
|
+
html, body { height: 100%; margin: 0; overflow: hidden; overscroll-behavior: none; background: var(--dsr-bg); color: var(--dsr-text); }
|
|
3
|
+
.ds-app { position: fixed; inset: 0; display: flex; overflow: hidden; }
|
|
4
|
+
.ds-sidebar { width: 280px; flex: none; display: flex; flex-direction: column; border-right: 1px solid var(--dsr-line); background: var(--dsr-panel); }
|
|
5
|
+
.ds-brand { display: flex; align-items: center; gap: 8px; padding: 14px 14px 10px; font-weight: 700; font-size: 15px; }
|
|
6
|
+
.ds-logo { color: var(--dsr-accent-strong); }
|
|
7
|
+
.ds-brand-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
8
|
+
.ds-gh { color: var(--dsr-muted); display: inline-flex; }
|
|
9
|
+
.ds-gh svg { width: 17px; height: 17px; fill: currentColor; }
|
|
10
|
+
.ds-new-session { padding: 0 12px 8px; }
|
|
11
|
+
.ds-btn { display: inline-flex; align-items: center; gap: 6px; background: var(--dsr-bg-2); color: var(--dsr-text); border: 1px solid var(--dsr-line); border-radius: 9px; padding: 6px 11px; font: inherit; font-size: 12.5px; cursor: pointer; white-space: nowrap; }
|
|
12
|
+
.ds-btn.primary { background: var(--dsr-accent-soft); border-color: var(--dsr-accent-line); color: var(--dsr-accent-strong); }
|
|
13
|
+
.ds-btn:hover { filter: brightness(1.06); }
|
|
14
|
+
.ds-btn:active { filter: brightness(.96); }
|
|
15
|
+
.ds-new-session .ds-btn { width: 100%; justify-content: center; }
|
|
16
|
+
.ds-section-label { font-size: 11px; color: var(--dsr-muted); letter-spacing: .8px; padding: 10px 14px 6px; font-weight: 700; }
|
|
17
|
+
.ds-session-list { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 8px; display: flex; flex-direction: column; gap: 3px; }
|
|
18
|
+
.ds-session-item { width: 100%; text-align: left; border: none; background: transparent; color: var(--dsr-text); font: inherit; font-size: 13px; padding: 8px 9px; border-radius: 9px; cursor: pointer; display: flex; flex-direction: column; gap: 3px; }
|
|
19
|
+
.ds-session-item:hover { background: var(--dsr-bg-2); }
|
|
20
|
+
.ds-session-item.current { background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); }
|
|
21
|
+
.ds-session-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
22
|
+
.ds-session-meta { display: flex; gap: 7px; font-size: 11px; color: var(--dsr-muted); align-items: center; }
|
|
23
|
+
.ds-session-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsr-line); }
|
|
24
|
+
.ds-session-dot.running { background: var(--dsr-warning); }
|
|
25
|
+
.ds-sidebar-foot { border-top: 1px solid var(--dsr-line); padding: 8px; display: flex; flex-direction: column; gap: 3px; }
|
|
26
|
+
.ds-nav-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; border: none; background: transparent; color: var(--dsr-text); font: inherit; font-size: 13px; padding: 9px 10px; border-radius: 9px; cursor: pointer; }
|
|
27
|
+
.ds-nav-item:hover { background: var(--dsr-bg-2); }
|
|
28
|
+
.ds-nav-item.active { background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); }
|
|
29
|
+
.ds-nav-ico { width: 18px; text-align: center; }
|
|
30
|
+
|
|
31
|
+
.ds-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
32
|
+
.ds-topbar { height: 52px; flex: none; display: flex; align-items: center; gap: 10px; padding: 0 14px; border-bottom: 1px solid var(--dsr-line); background: var(--dsr-panel); }
|
|
33
|
+
.ds-icon-btn { width: 32px; height: 32px; border-radius: 9px; border: 1px solid var(--dsr-line); background: var(--dsr-bg-2); color: var(--dsr-text); cursor: pointer; font-size: 15px; display: inline-flex; align-items: center; justify-content: center; }
|
|
34
|
+
.ds-icon-btn:hover { filter: brightness(1.06); }
|
|
35
|
+
#btn-mobile-nav { display: none; }
|
|
36
|
+
.ds-title { flex: 1; min-width: 0; font-weight: 700; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
37
|
+
.ds-top-right { display: flex; align-items: center; gap: 10px; }
|
|
38
|
+
.ds-conn { font-size: 11px; color: var(--dsr-muted); }
|
|
39
|
+
.ds-conn.on { color: var(--dsr-success); }
|
|
40
|
+
.ds-conn.ing { color: var(--dsr-warning); }
|
|
41
|
+
.ds-server { font-size: 12px; color: var(--dsr-muted); max-width: 320px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
42
|
+
|
|
43
|
+
.ds-content { flex: 1; min-height: 0; display: flex; }
|
|
44
|
+
.ds-view { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 14px 16px; overflow: hidden; }
|
|
45
|
+
.ds-view.hidden { display: none; }
|
|
46
|
+
.ds-view { animation: dsFade .15s ease; }
|
|
47
|
+
@keyframes dsFade { from { opacity: .5; } to { opacity: 1; } }
|
|
48
|
+
|
|
49
|
+
/* 会话视图 */
|
|
50
|
+
#view-chat { padding: 0; }
|
|
51
|
+
.ds-history { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 18px 22px; display: flex; flex-direction: column; gap: 10px; }
|
|
52
|
+
.ds-msg { max-width: 78%; padding: 9px 12px; border-radius: 12px; font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }
|
|
53
|
+
.ds-msg.user { align-self: flex-end; background: var(--dsr-accent-soft); border: 1px solid var(--dsr-accent-line); color: var(--dsr-accent-strong); }
|
|
54
|
+
.ds-msg.assistant { align-self: flex-start; background: var(--dsr-panel); border: 1px solid var(--dsr-line); color: var(--dsr-text); }
|
|
55
|
+
.ds-msg .role { font-size: 10px; color: var(--dsr-muted); margin-bottom: 3px; }
|
|
56
|
+
.ds-tool { align-self: flex-start; max-width: 78%; background: var(--dsr-bg-2); border: 1px solid var(--dsr-line); border-radius: 10px; padding: 8px 11px; font-size: 12px; }
|
|
57
|
+
.ds-tool summary { cursor: pointer; color: var(--dsr-muted); }
|
|
58
|
+
.ds-tool pre { margin: 6px 0 0; white-space: pre-wrap; word-break: break-all; font-size: 11px; color: var(--dsr-text); }
|
|
59
|
+
.ds-empty { color: var(--dsr-muted); text-align: center; padding: 30px 0; font-size: 13px; }
|
|
60
|
+
.ds-composer { flex: none; display: flex; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--dsr-line); background: var(--dsr-panel); }
|
|
61
|
+
.ds-composer textarea { flex: 1; min-width: 0; resize: none; background: var(--dsr-bg-2); color: var(--dsr-text); border: 1px solid var(--dsr-line); border-radius: 11px; padding: 10px 12px; font: inherit; font-size: 13.5px; outline: none; max-height: 160px; }
|
|
62
|
+
|
|
63
|
+
/* 文件传输 */
|
|
64
|
+
.ds-fs-bar { display: flex; align-items: center; gap: 8px; margin: 8px 0; }
|
|
65
|
+
.ds-fs-path { flex: 1; min-width: 0; font-size: 12.5px; color: var(--dsr-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
66
|
+
.ds-fs-list { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-panel); }
|
|
67
|
+
.ds-fs-row { display: flex; align-items: center; gap: 9px; padding: 9px 12px; border-bottom: 1px solid var(--dsr-divider); font-size: 13px; }
|
|
68
|
+
.ds-fs-row:last-child { border-bottom: none; }
|
|
69
|
+
.ds-fs-row:hover { background: var(--dsr-bg-2); }
|
|
70
|
+
.ds-fs-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
71
|
+
.ds-fs-size { color: var(--dsr-muted); font-size: 12px; white-space: nowrap; }
|
|
72
|
+
|
|
73
|
+
/* 设置 */
|
|
74
|
+
.ds-settings { max-width: 720px; display: flex; flex-direction: column; gap: 10px; }
|
|
75
|
+
.ds-setting-row { display: flex; align-items: center; gap: 12px; background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 12px; padding: 11px 13px; }
|
|
76
|
+
.ds-setting-row > div:first-child { flex: 1; min-width: 0; }
|
|
77
|
+
.ds-setting-name { font-size: 13.5px; font-weight: 600; }
|
|
78
|
+
.ds-setting-desc { font-size: 12px; color: var(--dsr-muted); margin-top: 2px; word-break: break-all; }
|
|
79
|
+
.ds-group-bar { display: flex; align-items: center; gap: 10px; padding: 2px 0; }
|
|
80
|
+
.ds-group-bar > label { font-size: 12px; color: var(--dsr-muted); white-space: nowrap; }
|
|
81
|
+
.ds-group-select { flex: 1; min-width: 0; position: relative; }
|
|
82
|
+
.ds-select-btn { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 6px; background: var(--dsr-panel); color: var(--dsr-text); border: 1px solid var(--dsr-line); border-radius: 10px; padding: 8px 10px; font: inherit; font-size: 13px; cursor: pointer; }
|
|
83
|
+
.ds-select-btn .caret { color: var(--dsr-muted); font-size: 10px; }
|
|
84
|
+
.ds-group-menu { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 40; max-height: 240px; overflow-y: auto; background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,.28); padding: 4px; display: flex; flex-direction: column; gap: 2px; }
|
|
85
|
+
.ds-group-menu.hidden { display: none; }
|
|
86
|
+
.ds-group-option { width: 100%; text-align: left; background: none; border: none; color: var(--dsr-text); font: inherit; font-size: 13px; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
|
|
87
|
+
.ds-group-option.current { background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); font-weight: 600; }
|
|
88
|
+
.ds-server-list { display: flex; flex-direction: column; gap: 7px; }
|
|
89
|
+
.ds-srv-group { border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-panel); overflow: hidden; }
|
|
90
|
+
.ds-srv-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; }
|
|
91
|
+
.ds-srv-name { flex: 1; min-width: 0; text-align: left; background: none; border: none; color: var(--dsr-text); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; padding: 2px 0; display: flex; align-items: center; gap: 6px; }
|
|
92
|
+
.ds-srv-count { font-size: 10.5px; color: var(--dsr-muted); background: var(--dsr-bg-2); border-radius: 999px; padding: 0 7px; line-height: 16px; }
|
|
93
|
+
.ds-srv-body { display: flex; flex-direction: column; gap: 6px; padding: 0 9px 9px; }
|
|
94
|
+
.ds-srv-body.hidden { display: none; }
|
|
95
|
+
.ds-server-row { display: flex; align-items: center; gap: 8px; background: var(--dsr-bg-2); border: 1px solid var(--dsr-line); border-radius: 10px; padding: 8px 10px; cursor: pointer; }
|
|
96
|
+
.ds-server-row.active { border-color: var(--dsr-info-line); background: var(--dsr-info-soft); }
|
|
97
|
+
.ds-server-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
98
|
+
.ds-server-note { font-size: 13px; word-break: break-all; }
|
|
99
|
+
.ds-server-url { font-size: 11.5px; color: var(--dsr-muted); word-break: break-all; }
|
|
100
|
+
.ds-server-badge { flex-shrink: 0; font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); border: 1px solid var(--dsr-accent-line); white-space: nowrap; }
|
|
101
|
+
.ds-server-badge.good { background: var(--dsr-success-soft); color: var(--dsr-success); border-color: var(--dsr-success-line); }
|
|
102
|
+
.ds-server-badge.bad { background: var(--dsr-warning-soft); color: var(--dsr-warning); border-color: var(--dsr-warning-line); }
|
|
103
|
+
.ds-server-add { display: flex; gap: 8px; }
|
|
104
|
+
.ds-server-add input { flex: 1; min-width: 0; background: var(--dsr-panel); color: var(--dsr-text); border: 1px solid var(--dsr-line); border-radius: 10px; padding: 8px 10px; font: inherit; font-size: 13px; outline: none; }
|
|
105
|
+
.ds-mini { width: 24px; height: 24px; border-radius: 7px; border: 1px solid var(--dsr-line); background: var(--dsr-panel); color: var(--dsr-muted); font-size: 12px; cursor: pointer; flex-shrink: 0; }
|
|
106
|
+
.ds-mini:hover { color: var(--dsr-text); }
|
|
107
|
+
.ds-switch { position: relative; display: inline-block; width: 36px; height: 21px; flex-shrink: 0; }
|
|
108
|
+
.ds-switch input { opacity: 0; width: 0; height: 0; }
|
|
109
|
+
.ds-switch .ds-slider { position: absolute; inset: 0; border-radius: 999px; background: var(--dsr-panel-2); border: 1px solid var(--dsr-line); transition: .2s; }
|
|
110
|
+
.ds-switch .ds-slider::before { content: ''; position: absolute; width: 15px; height: 15px; border-radius: 50%; left: 2px; top: 2px; background: var(--dsr-muted); transition: .2s; }
|
|
111
|
+
.ds-switch input:checked + .ds-slider { background: var(--dsr-accent-soft); border-color: var(--dsr-accent); }
|
|
112
|
+
.ds-switch input:checked + .ds-slider::before { transform: translateX(15px); background: var(--dsr-accent); }
|
|
113
|
+
|
|
114
|
+
/* 统计 */
|
|
115
|
+
.ds-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
|
|
116
|
+
.ds-stat-card { background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 12px; padding: 13px 15px; }
|
|
117
|
+
.ds-stat-card .v { font-size: 20px; font-weight: 700; }
|
|
118
|
+
.ds-stat-card .k { font-size: 11.5px; color: var(--dsr-muted); margin-top: 3px; line-height: 1.5; }
|
|
119
|
+
.ds-bucket-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 12px; margin-top: 6px; }
|
|
120
|
+
.ds-bucket-grid .b { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; }
|
|
121
|
+
.ds-bucket-grid .n { color: var(--dsr-muted); }
|
|
122
|
+
.ds-bucket-grid .t { font-weight: 600; }
|
|
123
|
+
.ds-stats-legend { display: flex; gap: 12px; font-size: 11px; color: var(--dsr-muted); margin: 8px 0; }
|
|
124
|
+
.ds-stats-legend .sw { width: 9px; height: 9px; border-radius: 2px; display: inline-block; margin-right: 4px; }
|
|
125
|
+
.ds-stats-legend .sw.peak { background: var(--dsr-accent-strong); }
|
|
126
|
+
.ds-stats-legend .sw.off { background: var(--dsr-accent-2); }
|
|
127
|
+
.ds-stats-chart { display: flex; align-items: flex-end; gap: 8px; height: 200px; padding: 12px 14px 10px; background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 12px; overflow-x: auto; }
|
|
128
|
+
.ds-stats-bar { flex: 1; min-width: 40px; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 5px; }
|
|
129
|
+
.ds-stats-bar .bars { width: 100%; max-width: 46px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; border-radius: 6px 6px 0 0; overflow: hidden; background: var(--dsr-accent-soft); }
|
|
130
|
+
.ds-stats-bar .seg { width: 100%; }
|
|
131
|
+
.ds-stats-bar .seg.peak { background: var(--dsr-accent-strong); }
|
|
132
|
+
.ds-stats-bar .seg.off { background: var(--dsr-accent-2); }
|
|
133
|
+
.ds-stats-bar .lbl { font-size: 10px; color: var(--dsr-muted); white-space: nowrap; }
|
|
134
|
+
.ds-stats-bar .val { font-size: 10px; white-space: nowrap; }
|
|
135
|
+
.ds-stats-note { font-size: 11px; color: var(--dsr-muted); line-height: 1.6; margin-top: 8px; }
|
|
136
|
+
|
|
137
|
+
/* 统计抽屉(右侧滑出) */
|
|
138
|
+
.ds-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 110; width: min(540px, 100vw); background: var(--dsr-panel); border-left: 1px solid var(--dsr-line); box-shadow: -12px 0 32px rgba(0,0,0,.35); display: flex; flex-direction: column; animation: dsDrawerIn .2s ease; }
|
|
139
|
+
.ds-drawer.hidden { display: none; }
|
|
140
|
+
@keyframes dsDrawerIn { from { transform: translateX(40px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
141
|
+
.ds-drawer-head { flex: none; display: flex; align-items: center; justify-content: space-between; padding: 13px 15px; border-bottom: 1px solid var(--dsr-line); font-weight: 700; font-size: 15px; }
|
|
142
|
+
.ds-drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 15px; scrollbar-width: thin; scrollbar-color: var(--dsr-line) transparent; }
|
|
143
|
+
.ds-drawer-body::-webkit-scrollbar { width: 8px; }
|
|
144
|
+
.ds-drawer-body::-webkit-scrollbar-thumb { background: var(--dsr-line); border-radius: 999px; }
|
|
145
|
+
.ds-drawer-body::-webkit-scrollbar-thumb:hover { background: var(--dsr-muted); }
|
|
146
|
+
|
|
147
|
+
/* 通知卡片栈(右下角) */
|
|
148
|
+
.ds-toast-stack { position: fixed; right: 16px; bottom: 16px; z-index: 100; display: flex; flex-direction: column; gap: 10px; width: min(380px, calc(100vw - 32px)); }
|
|
149
|
+
.ds-notif-card { background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-left: 3px solid var(--dsr-accent-strong); border-radius: 12px; box-shadow: 0 10px 28px rgba(0,0,0,.3); padding: 11px 13px; animation: dsSlideIn .22s ease; }
|
|
150
|
+
.ds-notif-card.question { border-left-color: var(--dsr-accent-2); }
|
|
151
|
+
@keyframes dsSlideIn { from { transform: translateX(30px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
152
|
+
.ds-notif-head { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--dsr-muted); }
|
|
153
|
+
.ds-notif-title { font-size: 14px; font-weight: 700; margin: 4px 0 3px; }
|
|
154
|
+
.ds-notif-body { font-size: 12.5px; color: var(--dsr-muted); line-height: 1.5; white-space: pre-wrap; word-break: break-word; max-height: 120px; overflow-y: auto; }
|
|
155
|
+
.ds-notif-actions { display: flex; gap: 8px; margin-top: 9px; }
|
|
156
|
+
.ds-notif-actions .ds-btn { flex: 1; justify-content: center; }
|
|
157
|
+
.ds-notif-actions .allow { background: var(--dsr-success-soft); border-color: var(--dsr-success-line); color: var(--dsr-success); }
|
|
158
|
+
.ds-notif-actions .reject { background: var(--dsr-warning-soft); border-color: var(--dsr-warning-line); color: var(--dsr-warning); }
|
|
159
|
+
|
|
160
|
+
/* 模态 */
|
|
161
|
+
.ds-modal { position: fixed; inset: 0; z-index: 120; background: rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center; padding: 16px; }
|
|
162
|
+
.ds-modal.hidden { display: none; }
|
|
163
|
+
.ds-modal-card { width: min(520px, 100%); background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 14px; padding: 16px; }
|
|
164
|
+
.ds-modal-title { font-weight: 700; margin-bottom: 10px; }
|
|
165
|
+
.ds-modal-body { display: flex; flex-direction: column; gap: 10px; max-height: 60vh; overflow-y: auto; }
|
|
166
|
+
.ds-modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
|
|
167
|
+
.ds-q-item { border: 1px solid var(--dsr-line); border-radius: 10px; padding: 9px 11px; }
|
|
168
|
+
.ds-q-text { font-size: 13.5px; margin-bottom: 6px; }
|
|
169
|
+
.ds-q-option { display: flex; gap: 7px; align-items: flex-start; font-size: 13px; padding: 3px 0; }
|
|
170
|
+
.ds-q-option .muted { font-size: 11px; color: var(--dsr-muted); }
|
|
171
|
+
.ds-modal-body textarea { width: 100%; box-sizing: border-box; margin-top: 6px; background: var(--dsr-bg-2); color: var(--dsr-text); border: 1px solid var(--dsr-line); border-radius: 9px; padding: 7px 9px; font: inherit; font-size: 13px; outline: none; }
|
|
172
|
+
|
|
173
|
+
.ds-toast { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 150; background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-radius: 10px; padding: 9px 15px; font-size: 13px; box-shadow: 0 8px 24px rgba(0,0,0,.3); }
|
|
174
|
+
.ds-toast.hidden { display: none; }
|
|
175
|
+
|
|
176
|
+
.mobile-only { display: none; }
|
|
177
|
+
|
|
178
|
+
/* 内置滚动条(去原生, 沿用皮肤变量) */
|
|
179
|
+
.ds-session-list, .ds-history, .ds-fs-list, .ds-group-menu, .ds-modal-body, .ds-notif-body { scrollbar-width: thin; scrollbar-color: var(--dsr-line) transparent; }
|
|
180
|
+
.ds-session-list::-webkit-scrollbar, .ds-history::-webkit-scrollbar, .ds-fs-list::-webkit-scrollbar,
|
|
181
|
+
.ds-group-menu::-webkit-scrollbar, .ds-modal-body::-webkit-scrollbar, .ds-notif-body::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
182
|
+
.ds-session-list::-webkit-scrollbar-track, .ds-history::-webkit-scrollbar-track, .ds-fs-list::-webkit-scrollbar-track,
|
|
183
|
+
.ds-group-menu::-webkit-scrollbar-track, .ds-modal-body::-webkit-scrollbar-track, .ds-notif-body::-webkit-scrollbar-track { background: transparent; }
|
|
184
|
+
.ds-session-list::-webkit-scrollbar-thumb, .ds-history::-webkit-scrollbar-thumb, .ds-fs-list::-webkit-scrollbar-thumb,
|
|
185
|
+
.ds-group-menu::-webkit-scrollbar-thumb, .ds-modal-body::-webkit-scrollbar-thumb, .ds-notif-body::-webkit-scrollbar-thumb {
|
|
186
|
+
background: var(--dsr-line); border-radius: 999px;
|
|
187
|
+
}
|
|
188
|
+
.ds-session-list::-webkit-scrollbar-thumb:hover, .ds-history::-webkit-scrollbar-thumb:hover, .ds-fs-list::-webkit-scrollbar-thumb:hover,
|
|
189
|
+
.ds-group-menu::-webkit-scrollbar-thumb:hover, .ds-modal-body::-webkit-scrollbar-thumb:hover, .ds-notif-body::-webkit-scrollbar-thumb:hover { background: var(--dsr-muted); }
|
|
190
|
+
|
|
191
|
+
/* 桌面 hover 态 */
|
|
192
|
+
@media (hover: hover) and (pointer: fine) {
|
|
193
|
+
.ds-session-item:hover, .ds-nav-item:hover, .ds-server-row:hover, .ds-fs-row:hover { background: var(--dsr-bg-2); }
|
|
194
|
+
.ds-btn:hover { filter: brightness(1.08); }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* 窄屏回退: 隐藏侧栏, 顶部导航, 通知卡片全宽 */
|
|
198
|
+
@media (max-width: 1023px) {
|
|
199
|
+
.ds-sidebar { display: none; }
|
|
200
|
+
#btn-mobile-nav { display: inline-flex; }
|
|
201
|
+
.ds-content { flex-direction: column; }
|
|
202
|
+
.ds-view { padding: 10px; overflow-y: auto; }
|
|
203
|
+
.ds-server { display: none; }
|
|
204
|
+
.ds-msg { max-width: 90%; }
|
|
205
|
+
.ds-toast-stack { left: 10px; right: 10px; width: auto; }
|
|
206
|
+
.mobile-only { display: block; }
|
|
207
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>DSH Remote · 桌面</title>
|
|
7
|
+
<script>/* 首帧前应用皮肤 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
8
|
+
<link rel="stylesheet" href="../theme-vars.css">
|
|
9
|
+
<link rel="stylesheet" href="desktop.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div class="ds-app" id="ds-app">
|
|
13
|
+
<aside class="ds-sidebar" id="ds-sidebar">
|
|
14
|
+
<div class="ds-brand">
|
|
15
|
+
<span class="ds-logo">◆</span><span class="ds-brand-name">DSH Remote</span>
|
|
16
|
+
<a class="ds-gh" href="https://github.com/Blank-not-black/dsh-Remote" target="_blank" rel="noopener" data-i18n-title="ds.repo" data-i18n-aria="ds.repo">
|
|
17
|
+
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8a8.01 8.01 0 0 0 5.47 7.59c.4.08.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
|
|
18
|
+
</a>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="ds-new-session">
|
|
21
|
+
<button id="btn-new-session" class="ds-btn primary" data-i18n="ds.newSession">+ 新会话</button>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="ds-section-label" data-i18n="ds.sessions">会话</div>
|
|
24
|
+
<div class="ds-session-list" id="session-list"></div>
|
|
25
|
+
<div class="ds-sidebar-foot">
|
|
26
|
+
<button class="ds-nav-item" data-view="view-files"><span class="ds-nav-ico">⇅</span><span data-i18n="ds.files">文件传输</span></button>
|
|
27
|
+
<button class="ds-nav-item" data-view="view-settings"><span class="ds-nav-ico">⚙</span><span data-i18n="ds.settings">设置</span></button>
|
|
28
|
+
</div>
|
|
29
|
+
</aside>
|
|
30
|
+
|
|
31
|
+
<div class="ds-main">
|
|
32
|
+
<header class="ds-topbar">
|
|
33
|
+
<button id="btn-mobile-nav" class="ds-icon-btn" data-i18n-aria="ds.menu" data-i18n-title="ds.menu">☰</button>
|
|
34
|
+
<div class="ds-title" id="ds-title" data-i18n="ds.sessions">会话</div>
|
|
35
|
+
<div class="ds-top-right">
|
|
36
|
+
<span class="ds-conn" id="conn-badge" title="">●</span>
|
|
37
|
+
<span class="ds-server" id="server-badge"></span>
|
|
38
|
+
<button id="btn-stats-top" class="ds-icon-btn" data-i18n-aria="ds.stats" data-i18n-title="ds.stats">📊</button>
|
|
39
|
+
</div>
|
|
40
|
+
</header>
|
|
41
|
+
|
|
42
|
+
<main class="ds-content">
|
|
43
|
+
<!-- 会话列表视图(窄屏回退用) -->
|
|
44
|
+
<section id="view-sessions" class="ds-view">
|
|
45
|
+
<div class="ds-section-label mobile-only" data-i18n="ds.sessions">会话</div>
|
|
46
|
+
<div id="mobile-session-list" class="ds-session-list"></div>
|
|
47
|
+
</section>
|
|
48
|
+
|
|
49
|
+
<!-- 会话详情 -->
|
|
50
|
+
<section id="view-chat" class="ds-view">
|
|
51
|
+
<div id="history" class="ds-history" aria-live="polite"></div>
|
|
52
|
+
<div class="ds-composer">
|
|
53
|
+
<textarea id="composer" rows="1" data-i18n-placeholder="ds.composerPlaceholder" placeholder="输入消息…"></textarea>
|
|
54
|
+
<button id="btn-send" class="ds-btn primary" data-i18n="ds.send">发送</button>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
|
|
58
|
+
<!-- 文件传输 -->
|
|
59
|
+
<section id="view-files" class="ds-view hidden">
|
|
60
|
+
<div class="ds-section-label" data-i18n="ds.files">文件传输</div>
|
|
61
|
+
<div class="ds-fs-bar">
|
|
62
|
+
<button id="fs-up" class="ds-btn" data-i18n="ds.fsUp">上级</button>
|
|
63
|
+
<span id="fs-path" class="ds-fs-path">/</span>
|
|
64
|
+
<button id="fs-refresh" class="ds-btn" data-i18n="ds.fsRefresh">刷新</button>
|
|
65
|
+
</div>
|
|
66
|
+
<div id="fs-list" class="ds-fs-list"></div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<!-- 设置 -->
|
|
70
|
+
<section id="view-settings" class="ds-view hidden">
|
|
71
|
+
<div class="ds-section-label" data-i18n="ds.settings">设置</div>
|
|
72
|
+
<div class="ds-settings">
|
|
73
|
+
<div class="ds-setting-row">
|
|
74
|
+
<div><div class="ds-setting-name" data-i18n="ds.serversTitle">服务器(分组)</div><div class="ds-setting-desc" id="server-desc"></div></div>
|
|
75
|
+
<button id="btn-server-speed" class="ds-btn" data-i18n="ds.speedTest">测速</button>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="ds-group-bar">
|
|
78
|
+
<label data-i18n="ds.currentGroup">当前组</label>
|
|
79
|
+
<div class="ds-group-select" id="group-select">
|
|
80
|
+
<button id="group-select-btn" class="ds-select-btn" type="button"><span id="group-select-label">默认</span><span class="caret">▾</span></button>
|
|
81
|
+
<div id="group-select-menu" class="ds-group-menu hidden"></div>
|
|
82
|
+
</div>
|
|
83
|
+
<button id="btn-group-add" class="ds-btn" data-i18n="ds.addGroup">+ 组</button>
|
|
84
|
+
</div>
|
|
85
|
+
<div id="server-list" class="ds-server-list"></div>
|
|
86
|
+
<div class="ds-server-add">
|
|
87
|
+
<input id="server-input" type="url" placeholder="http://IP:8787">
|
|
88
|
+
<button id="btn-server-add" class="ds-btn" data-i18n="ds.addServer">添加</button>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="ds-setting-row">
|
|
91
|
+
<div><div class="ds-setting-name" data-i18n="ds.tokenTitle">访问令牌</div><div class="ds-setting-desc" id="token-desc"></div></div>
|
|
92
|
+
<button id="btn-copy-token" class="ds-btn" data-i18n="ds.copyToken">复制</button>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="ds-setting-row">
|
|
95
|
+
<div><div class="ds-setting-name" data-i18n="ds.themeTitle">皮肤</div><div class="ds-setting-desc" data-i18n="ds.themeDesc">四套皮肤</div></div>
|
|
96
|
+
<button id="btn-theme" class="ds-btn">默认深空</button>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="ds-setting-row">
|
|
99
|
+
<div><div class="ds-setting-name" data-i18n="ds.langTitle">语言 / Language</div></div>
|
|
100
|
+
<button id="btn-lang" class="ds-btn">EN</button>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</section>
|
|
104
|
+
|
|
105
|
+
</main>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- 右下角审批/提问通知卡片栈 -->
|
|
109
|
+
<div class="ds-toast-stack" id="notif-stack" aria-live="polite"></div>
|
|
110
|
+
|
|
111
|
+
<!-- 统计抽屉 -->
|
|
112
|
+
<div class="ds-drawer hidden" id="stats-drawer" aria-hidden="true">
|
|
113
|
+
<div class="ds-drawer-head">
|
|
114
|
+
<span data-i18n="ds.stats">统计</span>
|
|
115
|
+
<button class="ds-mini" id="stats-drawer-close" data-i18n-aria="ds.ignore" data-i18n-title="ds.ignore">✕</button>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="ds-drawer-body">
|
|
118
|
+
<div class="ds-stat-grid" id="stats-cards"></div>
|
|
119
|
+
<div id="stats-legend" class="ds-stats-legend"></div>
|
|
120
|
+
<div id="stats-chart" class="ds-stats-chart"></div>
|
|
121
|
+
<div id="stats-note" class="ds-stats-note"></div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- 提问回答模态(多问题仍用模态) -->
|
|
127
|
+
<div id="modal-question" class="ds-modal hidden">
|
|
128
|
+
<div class="ds-modal-card">
|
|
129
|
+
<div class="ds-modal-title" data-i18n="ds.questionTitle">回答问题</div>
|
|
130
|
+
<div id="question-body" class="ds-modal-body"></div>
|
|
131
|
+
<div class="ds-modal-actions">
|
|
132
|
+
<button id="btn-question-cancel" class="ds-btn" data-i18n="ds.ignore">忽略</button>
|
|
133
|
+
<button id="btn-question-submit" class="ds-btn primary" data-i18n="ds.submit">提交</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div id="toast" class="ds-toast hidden"></div>
|
|
139
|
+
|
|
140
|
+
<script>
|
|
141
|
+
window.DESKTOP_STR = {
|
|
142
|
+
zh: {
|
|
143
|
+
'ds.repo': 'GitHub 仓库', 'ds.newSession': '+ 新会话', 'ds.sessions': '会话',
|
|
144
|
+
'ds.files': '文件传输', 'ds.settings': '设置', 'ds.stats': '统计', 'ds.menu': '导航',
|
|
145
|
+
'ds.send': '发送', 'ds.composerPlaceholder': '输入消息,Enter 发送…',
|
|
146
|
+
'ds.fsUp': '上级', 'ds.fsRefresh': '刷新', 'ds.fsEmpty': '目录为空',
|
|
147
|
+
'ds.serversTitle': '服务器(分组)', 'ds.speedTest': '测速', 'ds.currentGroup': '当前组',
|
|
148
|
+
'ds.addGroup': '+ 组', 'ds.addServer': '添加', 'ds.tokenTitle': '访问令牌', 'ds.copyToken': '复制',
|
|
149
|
+
'ds.themeTitle': '皮肤', 'ds.themeDesc': '默认深空 / 落日 / 易北爱乐厅 / 草原孤塔', 'ds.langTitle': '语言 / Language',
|
|
150
|
+
'ds.questionTitle': '回答问题', 'ds.ignore': '忽略', 'ds.submit': '提交',
|
|
151
|
+
'ds.connOn': '已连接', 'ds.connOff': '未连接', 'ds.connIng': '连接中',
|
|
152
|
+
'ds.currentServer': '{group} · {url}', 'ds.origin': '当前页面',
|
|
153
|
+
'ds.toastSent': '已发送', 'ds.toastCopied': '令牌已复制', 'ds.toastAuth': '令牌无效',
|
|
154
|
+
'ds.toastConnFailed': '连接失败', 'ds.toastOpFailed': '操作失败',
|
|
155
|
+
'ds.sessionsEmpty': '暂无会话', 'ds.loading': '加载中…',
|
|
156
|
+
'ds.historyLoading': '加载历史…', 'ds.historyEmpty': '暂无消息',
|
|
157
|
+
'ds.allow': '允许', 'ds.reject': '拒绝',
|
|
158
|
+
'ds.approvalTitle': '工具审批', 'ds.questionNotify': '需要回答',
|
|
159
|
+
'ds.approvalBody': '{tool} · {server}', 'ds.approvalReason': '原因:{reason}',
|
|
160
|
+
'ds.allowed': '已允许', 'ds.rejected': '已拒绝', 'ds.ignored': '已忽略',
|
|
161
|
+
'ds.stale': '请求已失效',
|
|
162
|
+
'ds.statsTodayTokens': '今日 Token', 'ds.statsTodayCost': '今日费用', 'ds.statsPeakShare': '高峰占比',
|
|
163
|
+
'ds.statsInput': '未缓存输入', 'ds.statsCacheRead': '缓存命中', 'ds.statsCacheWrite': '缓存写入', 'ds.statsOutput': '输出',
|
|
164
|
+
'ds.statsPeak': '高峰', 'ds.statsOff': '空闲', 'ds.statsDays': '近 {n} 天',
|
|
165
|
+
'ds.statsGatewayDown': '统计需要网关运行', 'ds.statsEmpty': '暂无统计',
|
|
166
|
+
'ds.statsNote': '注:本数据仅在使用 DeepSeek 官方 API 时估算;基于 token 计算,与官网账单可能有出入,一切以官网为准。统计自 2026-08-17 定价生效日起。',
|
|
167
|
+
'ds.serversEmpty': '未添加备用服务器', 'ds.serversUntested': '未测速', 'ds.serversCurrent': ' · 当前',
|
|
168
|
+
'ds.serversUnreachable': '不可达', 'ds.serversDelete': '删除', 'ds.serversEdit': '编辑',
|
|
169
|
+
'ds.serversNeedAddress': '请输入服务器地址', 'ds.serversBadProtocol': '地址需以 http:// 或 https:// 开头',
|
|
170
|
+
'ds.serversDuplicate': '该地址已存在', 'ds.serversAdded': '已添加', 'ds.serversEdited': '已更新',
|
|
171
|
+
'ds.serversRemovedActive': '已删除当前服务器,重新测速…',
|
|
172
|
+
'ds.serversCurrentDesc': '当前:{group} · {url}({ms})',
|
|
173
|
+
'ds.serversPromptNote': '服务器备注(可留空):', 'ds.serversPromptEditUrl': '编辑服务器地址:',
|
|
174
|
+
'ds.serversPromptEditNote': '编辑备注({url}):', 'ds.serversPromptEditGroup': '所属组:',
|
|
175
|
+
'ds.serversManualSelected': '已切换至:{url}',
|
|
176
|
+
'ds.speedTesting': '正在测速…', 'ds.speedSwitched': '已切换到:{url}({ms}ms)',
|
|
177
|
+
'ds.speedAlreadyBest': '当前已是最快:{url}({ms}ms)', 'ds.speedAllDown': '全部服务器不可达',
|
|
178
|
+
'ds.speedManualUsing': '手动模式:{url}({ms})',
|
|
179
|
+
'ds.groupsNoServer': '组内暂无服务器', 'ds.groupsAutoSelect': '自动选择最快', 'ds.groupsDelete': '删除组',
|
|
180
|
+
'ds.groupsAutoOn': '{group} 已切换为自动选择', 'ds.groupsAutoOff': '{group} 已切换为手动选择',
|
|
181
|
+
'ds.groupsSwitched': '已切换到组:{group}', 'ds.groupsSwitchHint': '点击切换当前组,双击重命名',
|
|
182
|
+
'ds.groupsPromptAdd': '新组名:', 'ds.groupsDuplicate': '组名已存在', 'ds.groupsAdded': '已创建组:{group}',
|
|
183
|
+
'ds.groupsPromptRename': '重命名组「{group}」:', 'ds.groupsRenamed': '组已重命名',
|
|
184
|
+
'ds.groupsCannotDeleteDefault': '默认组不可删除',
|
|
185
|
+
'ds.groupsConfirmDelete': '删除组「{group}」?组内服务器将归入默认组。', 'ds.groupsDeleted': '组已删除',
|
|
186
|
+
'ds.theme.default': '默认深空', 'ds.theme.dark': '落日', 'ds.theme.light': '易北爱乐厅', 'ds.theme.neutral': '草原孤塔',
|
|
187
|
+
'ds.role.me': '我', 'ds.role.dsh': 'DSH', 'ds.toolDefault': '工具',
|
|
188
|
+
'ds.questionOptions': '选项', 'ds.questionCustom': '自定义回答',
|
|
189
|
+
'ds.questionNeedAnswer': '请选择一个选项或填写自定义回答', 'ds.questionSubmitted': '已提交回答',
|
|
190
|
+
'ds.questionFrom': '来自 {server}',
|
|
191
|
+
},
|
|
192
|
+
en: {
|
|
193
|
+
'ds.repo': 'GitHub repo', 'ds.newSession': '+ New session', 'ds.sessions': 'Sessions',
|
|
194
|
+
'ds.files': 'Files', 'ds.settings': 'Settings', 'ds.stats': 'Stats', 'ds.menu': 'Menu',
|
|
195
|
+
'ds.send': 'Send', 'ds.composerPlaceholder': 'Type a message, Enter to send…',
|
|
196
|
+
'ds.fsUp': 'Up', 'ds.fsRefresh': 'Refresh', 'ds.fsEmpty': 'Empty directory',
|
|
197
|
+
'ds.serversTitle': 'Servers (groups)', 'ds.speedTest': 'Test', 'ds.currentGroup': 'Current group',
|
|
198
|
+
'ds.addGroup': '+ Group', 'ds.addServer': 'Add', 'ds.tokenTitle': 'Access token', 'ds.copyToken': 'Copy',
|
|
199
|
+
'ds.themeTitle': 'Theme', 'ds.themeDesc': 'Deep Space / Sunset / Elbphilharmonie / Prairie Tower', 'ds.langTitle': 'Language',
|
|
200
|
+
'ds.questionTitle': 'Answer question', 'ds.ignore': 'Ignore', 'ds.submit': 'Submit',
|
|
201
|
+
'ds.connOn': 'Connected', 'ds.connOff': 'Offline', 'ds.connIng': 'Connecting',
|
|
202
|
+
'ds.currentServer': '{group} · {url}', 'ds.origin': 'this page',
|
|
203
|
+
'ds.toastSent': 'Sent', 'ds.toastCopied': 'Token copied', 'ds.toastAuth': 'Invalid token',
|
|
204
|
+
'ds.toastConnFailed': 'Connection failed', 'ds.toastOpFailed': 'Operation failed',
|
|
205
|
+
'ds.sessionsEmpty': 'No sessions', 'ds.loading': 'Loading…',
|
|
206
|
+
'ds.historyLoading': 'Loading history…', 'ds.historyEmpty': 'No messages yet',
|
|
207
|
+
'ds.allow': 'Allow', 'ds.reject': 'Reject',
|
|
208
|
+
'ds.approvalTitle': 'Tool approval', 'ds.questionNotify': 'Question',
|
|
209
|
+
'ds.approvalBody': '{tool} · {server}', 'ds.approvalReason': 'Reason: {reason}',
|
|
210
|
+
'ds.allowed': 'Allowed', 'ds.rejected': 'Rejected', 'ds.ignored': 'Ignored',
|
|
211
|
+
'ds.stale': 'Request is stale',
|
|
212
|
+
'ds.statsTodayTokens': 'Tokens today', 'ds.statsTodayCost': 'Cost today', 'ds.statsPeakShare': 'Peak share',
|
|
213
|
+
'ds.statsInput': 'Uncached input', 'ds.statsCacheRead': 'Cache read', 'ds.statsCacheWrite': 'Cache write', 'ds.statsOutput': 'Output',
|
|
214
|
+
'ds.statsPeak': 'Peak', 'ds.statsOff': 'Off-peak', 'ds.statsDays': 'Last {n} days',
|
|
215
|
+
'ds.statsGatewayDown': 'Stats require the gateway', 'ds.statsEmpty': 'No stats yet',
|
|
216
|
+
'ds.statsNote': 'Note: estimates assume the official DeepSeek API. Token-based calculation may differ from the official bill; always defer to deepseek.com. Stats start from the 2026-08-17 pricing date.',
|
|
217
|
+
'ds.serversEmpty': 'No backup servers', 'ds.serversUntested': 'Untested', 'ds.serversCurrent': ' · current',
|
|
218
|
+
'ds.serversUnreachable': 'Unreachable', 'ds.serversDelete': 'Delete', 'ds.serversEdit': 'Edit',
|
|
219
|
+
'ds.serversNeedAddress': 'Enter a server address', 'ds.serversBadProtocol': 'Address must start with http:// or https://',
|
|
220
|
+
'ds.serversDuplicate': 'Address already exists', 'ds.serversAdded': 'Added', 'ds.serversEdited': 'Updated',
|
|
221
|
+
'ds.serversRemovedActive': 'Removed active server, re-testing…',
|
|
222
|
+
'ds.serversCurrentDesc': 'Current: {group} · {url} ({ms})',
|
|
223
|
+
'ds.serversPromptNote': 'Server note (optional):', 'ds.serversPromptEditUrl': 'Edit server URL:',
|
|
224
|
+
'ds.serversPromptEditNote': 'Edit note ({url}):', 'ds.serversPromptEditGroup': 'Group:',
|
|
225
|
+
'ds.serversManualSelected': 'Switched to: {url}',
|
|
226
|
+
'ds.speedTesting': 'Testing servers…', 'ds.speedSwitched': 'Switched to: {url} ({ms}ms)',
|
|
227
|
+
'ds.speedAlreadyBest': 'Already on fastest: {url} ({ms}ms)', 'ds.speedAllDown': 'All servers unreachable',
|
|
228
|
+
'ds.speedManualUsing': 'Manual mode: {url} ({ms})',
|
|
229
|
+
'ds.groupsNoServer': 'No servers in this group', 'ds.groupsAutoSelect': 'Auto-select fastest', 'ds.groupsDelete': 'Delete group',
|
|
230
|
+
'ds.groupsAutoOn': '{group} is now auto-select', 'ds.groupsAutoOff': '{group} is now manual',
|
|
231
|
+
'ds.groupsSwitched': 'Switched to group: {group}', 'ds.groupsSwitchHint': 'Click to switch, double-click to rename',
|
|
232
|
+
'ds.groupsPromptAdd': 'New group name:', 'ds.groupsDuplicate': 'Group already exists', 'ds.groupsAdded': 'Created group: {group}',
|
|
233
|
+
'ds.groupsPromptRename': 'Rename group "{group}":', 'ds.groupsRenamed': 'Group renamed',
|
|
234
|
+
'ds.groupsCannotDeleteDefault': 'The default group cannot be deleted',
|
|
235
|
+
'ds.groupsConfirmDelete': 'Delete group "{group}"? Its servers move to the default group.', 'ds.groupsDeleted': 'Group deleted',
|
|
236
|
+
'ds.theme.default': 'Deep Space', 'ds.theme.dark': 'Sunset', 'ds.theme.light': 'Elbphilharmonie', 'ds.theme.neutral': 'Prairie Tower',
|
|
237
|
+
'ds.role.me': 'Me', 'ds.role.dsh': 'DSH', 'ds.toolDefault': 'tool',
|
|
238
|
+
'ds.questionOptions': 'Options', 'ds.questionCustom': 'Custom answer',
|
|
239
|
+
'ds.questionNeedAnswer': 'Choose an option or write a custom answer', 'ds.questionSubmitted': 'Answer submitted',
|
|
240
|
+
'ds.questionFrom': 'From {server}',
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
</script>
|
|
244
|
+
<script src="i18n.js"></script>
|
|
245
|
+
<script src="desktop.js"></script>
|
|
246
|
+
</body>
|
|
247
|
+
</html>
|