dsh-remote-plugin 0.6.7 → 0.6.9

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/index.mjs CHANGED
@@ -151,7 +151,12 @@ function runExit(cmd, args) {
151
151
  const GATEWAY_ENV_KEYS = [
152
152
  'TOKEN', 'TOKEN_FILE', 'DSH_REMOTE_TOKEN', 'DSH_REMOTE_FS_ROOT', 'DSH_REMOTE_FS_MAX_UPLOAD',
153
153
  'DSH_REMOTE_NOTES', 'DSH_REMOTE_WORKBENCH', 'DSH_REMOTE_DSH_SERVICE', 'DSH_REMOTE_FEEDBACK_URL',
154
- 'UPDATE_CHECK_URL', 'UPDATE_INTERVAL_MS', 'UPDATE_PROXY', 'GATEWAY_WS_IDLE_MS',
154
+ 'UPDATE_CHECK_URL', 'UPDATE_INTERVAL_MS', 'UPDATE_PROXY', 'DSH_HEALTH_PATH',
155
+ 'GATEWAY_WS_IDLE_MS', 'GATEWAY_WS_PING_MS', 'GATEWAY_WS_PONG_TIMEOUT_MS',
156
+ 'GATEWAY_WS_UPGRADE_TIMEOUT_MS', 'GATEWAY_UPSTREAM_TIMEOUT_MS', 'GATEWAY_EVENT_BUFFER_MAX',
157
+ 'GATEWAY_WS_TICKET_TTL_MS',
158
+ 'GATEWAY_HTTP_REQUEST_TIMEOUT_MS', 'GATEWAY_HTTP_HEADERS_TIMEOUT_MS', 'GATEWAY_HTTP_KEEPALIVE_TIMEOUT_MS',
159
+ 'DSH_REMOTE_CORS_ORIGINS',
155
160
  'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY',
156
161
  'http_proxy', 'https_proxy', 'all_proxy', 'no_proxy'
157
162
  ]
@@ -194,6 +199,9 @@ async function gatewayRunning() {
194
199
  version: typeof data.version === 'string' ? data.version : '',
195
200
  upstream: typeof data.upstream === 'string' ? data.upstream : '',
196
201
  upstreamOk: data.upstreamOk === true,
202
+ upstreamReachable: data.upstreamReachable !== false,
203
+ upstreamStatus: Number(data.upstreamStatus) || 0,
204
+ upstreamProbe: typeof data.upstreamProbe === 'string' ? data.upstreamProbe : '',
197
205
  }
198
206
  } catch {
199
207
  return { running: false }
@@ -344,7 +352,9 @@ function ensureGateway() {
344
352
  const oldUpstream = health.upstream || ''
345
353
  const oldVersion = health.version || '?'
346
354
  const versionMismatch = oldVersion !== version
347
- if (versionMismatch || health.upstreamOk === false || (oldUpstream && oldUpstream !== upstream) || (!oldUpstream && upstream)) {
355
+ // 上游暂时不可达不应重启网关: VPN/DSH 重启/短暂网络抖动时,
356
+ // 重启只能制造额外断线,网关应保持运行并通过 /health 暴露 degraded 状态。
357
+ if (versionMismatch || (oldUpstream && oldUpstream !== upstream) || (!oldUpstream && upstream)) {
348
358
  logGateway(`网关需刷新: 版本 ${oldVersion} -> ${version}, 上游 ${oldUpstream || '?'} -> ${upstream}`)
349
359
  await killGateway(health)
350
360
  for (let i = 0; i < 10; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-plugin",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "DSH Remote 官方 bundle 插件:DSH 左侧原生边栏入口 + 右侧抽屉内嵌管理控制台;内置网关随 DSH 自动启停(systemd 独立单元),抽屉直显令牌与设备监控;网关提供 /fs/* 文件传输端点(列表/断点下载/上传),配合 Android App 远程操控会话/审批/提问/goal 与文件互传(多服务器测速切换、聊天记录离线缓存)。",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
package/public/admin.html CHANGED
@@ -145,7 +145,7 @@
145
145
  padding: 7px 8px; border-radius: 10px; border: none; background: transparent; color: var(--dsr-text);
146
146
  font: inherit; font-size: 13px; text-align: left; cursor: pointer; text-decoration: none;
147
147
  }
148
- .fb-item:hover, .fb-item:focus-visible { background: var(--dsr-bg); outline: none; }
148
+ .fb-item:hover, .fb-item:focus-visible { background: var(--dsr-bg); }
149
149
  .fb-item.primary { background: var(--dsr-accent-soft); border: 1px solid var(--dsr-accent-line); }
150
150
  .fb-ico {
151
151
  width: 32px; height: 32px; border-radius: 9px; flex-shrink: 0;
@@ -174,6 +174,86 @@
174
174
  .admin-title .right { gap: 4px; }
175
175
  .tb-btn { padding: 0 8px; }
176
176
  }
177
+ .login-card input:focus-visible, .gateway-port-row input:focus-visible {
178
+ outline: 2px solid var(--dsr-accent);
179
+ outline-offset: 2px;
180
+ }
181
+ @media (prefers-reduced-motion: reduce) {
182
+ *, *::before, *::after {
183
+ animation-duration: .01ms !important;
184
+ animation-iteration-count: 1 !important;
185
+ transition-duration: .01ms !important;
186
+ }
187
+ }
188
+
189
+ /* 控制台面板:减少整面纯色,建立“工具台”层次 */
190
+ html, body { font-family: system-ui, -apple-system, "PingFang SC", "Noto Sans CJK SC", sans-serif; text-rendering: optimizeLegibility; }
191
+ body { background: radial-gradient(900px 520px at 92% -12%, var(--dsr-accent-soft), transparent 68%), linear-gradient(180deg, var(--dsr-bg), var(--dsr-bg-2)); }
192
+ .admin-wrap { max-width: 1320px; padding: calc(env(safe-area-inset-top, 0px) + 18px) clamp(14px, 3vw, 30px) 48px; }
193
+ .admin-title { margin-bottom: 18px; padding: 12px 14px; border: 1px solid var(--dsr-line); border-radius: 18px; background: linear-gradient(135deg, var(--dsr-panel), var(--dsr-bg-2)); box-shadow: 0 14px 36px var(--dsr-shadow); backdrop-filter: blur(16px); }
194
+ .admin-title h1 { font-size: 21px; letter-spacing: -.2px; }
195
+ .admin-title .left, .admin-title .right { gap: 7px; }
196
+ .tb-btn { border-radius: 11px; background: linear-gradient(135deg, var(--dsr-panel-2), var(--dsr-bg-2)); border-color: var(--dsr-line); transition: transform .18s ease, border-color .18s ease, background .18s ease; }
197
+ .tb-btn:hover { transform: translateY(-1px); border-color: var(--dsr-accent-line); background: var(--dsr-accent-soft); }
198
+ .tb-btn svg { width: 17px; height: 17px; }
199
+ .login-card, .stat-card, .stats-chart, .table-wrap, .gateway-port-row, .pair-box { background: linear-gradient(145deg, var(--dsr-panel), var(--dsr-bg-2)); border-color: var(--dsr-line); border-radius: 18px; box-shadow: 0 12px 30px var(--dsr-shadow); }
200
+ .login-card { padding: 18px; }
201
+ .stat-grid { gap: 12px; }
202
+ .stat-card { padding: 15px 16px; box-shadow: inset 0 1px var(--dsr-divider), 0 10px 26px var(--dsr-shadow); }
203
+ .stat-card .v { font-size: 24px; font-variant-numeric: tabular-nums; }
204
+ .stats-dash { margin-top: 4px; }
205
+ .stats-chart { height: 210px; padding: 16px 18px 12px; }
206
+ .stats-bar .bars { border-radius: 9px 9px 3px 3px; box-shadow: 0 0 16px var(--dsr-glow); }
207
+ .section-head { margin: 18px 2px 9px; font-weight: 750; letter-spacing: .2px; }
208
+ .token-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 13px; margin: 0 0 14px; border: 1px solid var(--dsr-line); border-radius: 18px; background: linear-gradient(145deg, var(--dsr-panel), var(--dsr-bg-2)); box-shadow: 0 10px 28px var(--dsr-shadow); }
209
+ .token-row code { width: auto; min-width: 0; padding: 10px 12px; border-radius: 11px; background: var(--dsr-bg); border-color: var(--dsr-line); }
210
+ .token-actions { justify-content: flex-end; }
211
+ .token-actions .mini-btn { flex: 0 0 auto; border-radius: 10px; }
212
+ .gateway-port-row { padding: 12px 14px; }
213
+ .table-wrap { overflow: hidden; }
214
+ th, td { border-bottom-color: var(--dsr-divider); }
215
+ tbody tr { transition: background .18s ease; }
216
+ .pair-box { padding: 18px; }
217
+ .admin-hero { display:flex; align-items:flex-end; justify-content:space-between; gap:18px; padding:20px; margin:0 0 16px; border:1px solid var(--dsr-accent-line); border-radius:20px; background:linear-gradient(135deg, var(--dsr-panel-2), var(--dsr-bg-2)); box-shadow:0 16px 34px var(--dsr-shadow); }
218
+ .admin-hero-copy { min-width:0; }
219
+ .admin-eyebrow { color:var(--dsr-accent-strong); font-size:10px; font-weight:800; letter-spacing:1.6px; }
220
+ .admin-hero h2 { margin:7px 0 4px; font-size:26px; line-height:1.15; letter-spacing:-.5px; }
221
+ .admin-hero p { margin:0; color:var(--dsr-muted); font-size:12px; }
222
+ .admin-hero-side { display:flex; flex-direction:column; align-items:flex-end; gap:10px; flex:none; }
223
+ .admin-hero-status { color:var(--dsr-success); font-size:12px; font-weight:700; }
224
+ .admin-hero.attention { border-color:var(--dsr-warning-line); }
225
+ .admin-hero.attention .admin-hero-status { color:var(--dsr-warning); }
226
+ .admin-hero.plugin, .admin-hero.offline { border-color:var(--dsr-line); }
227
+ .admin-hero.plugin .admin-hero-status, .admin-hero.offline .admin-hero-status { color:var(--dsr-muted); }
228
+ .admin-hero-action { min-height:36px; padding:7px 13px; border-color:var(--dsr-accent-strong); background:var(--dsr-accent-strong); color:var(--dsr-on-accent); font-weight:700; }
229
+ .admin-hero.plugin .admin-hero-action { border-color:var(--dsr-accent-strong); background:var(--dsr-accent-strong); }
230
+ .admin-hero.offline .admin-hero-action { border-color:var(--dsr-accent-line); background:var(--dsr-accent-soft); color:var(--dsr-accent-strong); }
231
+ #stats { grid-template-columns:repeat(4,minmax(0,1fr)); }
232
+ #stats .stat-card:nth-child(n+5) { background:transparent; box-shadow:none; }
233
+ #stats .stat-card:nth-child(n+5) .v { font-size:20px; }
234
+ @media (max-width: 760px) {
235
+ .admin-hero { align-items:stretch; flex-direction:column; gap:14px; padding:16px; }
236
+ .admin-hero h2 { font-size:23px; }
237
+ .admin-hero-side { align-items:stretch; flex-direction:row; justify-content:space-between; }
238
+ .admin-hero-action { flex:0 0 auto; }
239
+ #stats { grid-template-columns:repeat(2,minmax(0,1fr)); }
240
+ #stats .stat-card:nth-child(n+5) { background:linear-gradient(145deg, var(--dsr-panel), var(--dsr-bg-2)); box-shadow:inset 0 1px var(--dsr-divider), 0 10px 26px var(--dsr-shadow); }
241
+ }
242
+ @media (max-width: 720px) {
243
+ .token-row { display: flex; flex-direction: column; align-items: stretch; }
244
+ .token-actions { justify-content: stretch; }
245
+ .token-actions .mini-btn { flex: 1 1 auto; }
246
+ }
247
+ @media (max-width: 900px) {
248
+ .admin-title { display: grid; grid-template-columns: minmax(0, 1fr) auto; row-gap: 10px; }
249
+ .admin-title .left { min-width: 0; }
250
+ .admin-title .right { grid-column: 1 / -1; justify-content: flex-start; min-width: 0; overflow-x: auto; scrollbar-width: none; }
251
+ .admin-title .right::-webkit-scrollbar { display: none; }
252
+ }
253
+ @media (max-width: 440px) {
254
+ .admin-title h1 { display: block; min-width: 0; overflow: visible; font-size: 18px; }
255
+ .admin-title .left { gap: 7px; }
256
+ }
177
257
  </style>
178
258
  </head>
179
259
  <body>
@@ -185,11 +265,11 @@
185
265
  <h1>DSH Remote <span data-i18n="admin">管理</span></h1>
186
266
  </div>
187
267
  <div class="right">
188
- <a class="mini-btn gh-btn tb-btn" href="https://github.com/Blank-not-black/dsh-Remote" target="_blank" rel="noopener" title="GitHub">
268
+ <a class="mini-btn gh-btn tb-btn" href="https://github.com/Blank-not-black/dsh-Remote" target="_blank" rel="noopener" title="GitHub" data-i18n-aria="repo">
189
269
  <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>
190
270
  <span class="gh-label" data-i18n="repo">仓库</span>
191
271
  </a>
192
- <button id="btn-donate" class="mini-btn tb-btn" data-i18n-title="donateTitle">🎁</button>
272
+ <button id="btn-donate" class="mini-btn tb-btn" data-i18n-title="donateTitle" data-i18n-aria="donateTitle"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8h11v6a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4Z"/><path d="M17 10h1.5a2.5 2.5 0 0 1 0 5H17M9 4c0 1 1 1.2 1 2M13 4c0 1 1 1.2 1 2M5 20h13"/></svg></button>
193
273
  <div class="fb-wrap">
194
274
  <button id="btn-feedback" class="mini-btn fb-btn tb-btn" data-i18n-title="feedbackTitle" data-i18n-aria="feedbackTitle" aria-haspopup="menu" aria-expanded="false">
195
275
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a8 8 0 0 1-8 8H5l-1.5 2L4 20a8 8 0 0 1-1-4 8 8 0 0 1 18-4Z"/></svg>
@@ -223,6 +303,18 @@
223
303
  </div>
224
304
 
225
305
  <div id="main-view" class="hidden">
306
+ <section id="admin-hero" class="admin-hero" aria-live="polite">
307
+ <div class="admin-hero-copy">
308
+ <div class="admin-eyebrow" data-i18n="hero.eyebrow">GATEWAY CONTROL</div>
309
+ <h2 id="admin-hero-title">正在检查网关</h2>
310
+ <p id="admin-hero-desc">读取运行状态与上游连接</p>
311
+ </div>
312
+ <div class="admin-hero-side">
313
+ <span id="admin-hero-status" class="admin-hero-status">—</span>
314
+ <button id="admin-hero-action" class="mini-btn admin-hero-action" type="button" data-i18n="hero.openDevices">查看设备</button>
315
+ </div>
316
+ </section>
317
+
226
318
  <div class="token-row">
227
319
  <code id="token-full">—</code>
228
320
  <div class="token-actions">
@@ -274,7 +366,7 @@
274
366
  </div>
275
367
 
276
368
  <!-- 皮肤选择模态 -->
277
- <div id="modal-theme" class="modal hidden">
369
+ <div id="modal-theme" class="modal hidden" role="dialog" aria-modal="true">
278
370
  <div class="modal-card">
279
371
  <div class="modal-title" data-i18n="theme.panelTitle">选择配色</div>
280
372
  <div id="theme-options" class="theme-panel-list"></div>
@@ -285,11 +377,11 @@
285
377
  </div>
286
378
 
287
379
  <!-- 赞赏支持模态 -->
288
- <div id="modal-donate" class="modal hidden">
380
+ <div id="modal-donate" class="modal hidden" role="dialog" aria-modal="true">
289
381
  <div class="modal-card">
290
382
  <div class="modal-title" data-i18n="donateTitle">赞赏支持</div>
291
383
  <div class="modal-body donate-body">
292
- <img src="donate.png" alt="" class="donate-img" style="display:block;margin:0 auto;max-width:100%;max-height:60vh;border-radius:8px">
384
+ <img src="../donate.png" alt="赞赏二维码" width="1152" height="1152" class="donate-img" style="display:block;margin:0 auto;max-width:100%;height:auto;max-height:60vh;border-radius:12px">
293
385
  <p class="donate-thanks" data-i18n="donateThanks" style="text-align:center;margin-top:12px">感谢你的支持 ☕</p>
294
386
  </div>
295
387
  <div class="modal-actions">
@@ -298,7 +390,7 @@
298
390
  </div>
299
391
  </div>
300
392
 
301
- <div id="toast" class="toast hidden"></div>
393
+ <div id="toast" class="toast hidden" role="status" aria-live="polite"></div>
302
394
  <script>
303
395
  window.ADMIN_STR = {
304
396
  zh: {
@@ -306,6 +398,8 @@
306
398
  'consoleTitle': '返回控制台',
307
399
  'collapse': '收起面板',
308
400
  'admin': '管理',
401
+ 'hero.eyebrow': 'GATEWAY CONTROL', 'hero.running': '网关运行正常', 'hero.attention': '网关需要关注', 'hero.plugin': 'DSH 插件已连接', 'hero.offline': '网关未运行', 'hero.checking': '正在检查网关',
402
+ 'hero.runningDesc': '{online} 台设备在线 · {requests} 次请求', 'hero.attentionDesc': 'DSH 上游暂时不可达,请打开诊断', 'hero.pluginDesc': '插件已连接 DSH,网关可按需启动', 'hero.offlineDesc': '启动网关后即可接受 App 和桌面端连接', 'hero.openDevices': '查看设备', 'hero.startGateway': '启动网关', 'hero.copyToken': '复制令牌',
309
403
  'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔',
310
404
  'theme.panelTitle': '选择配色', 'theme.close': '关闭',
311
405
  'repo': '仓库',
@@ -389,6 +483,8 @@
389
483
  'consoleTitle': 'Back to console',
390
484
  'collapse': 'Collapse panel',
391
485
  'admin': 'Admin',
486
+ 'hero.eyebrow': 'GATEWAY CONTROL', 'hero.running': 'Gateway is healthy', 'hero.attention': 'Gateway needs attention', 'hero.plugin': 'DSH plugin connected', 'hero.offline': 'Gateway is not running', 'hero.checking': 'Checking gateway',
487
+ 'hero.runningDesc': '{online} devices online · {requests} requests', 'hero.attentionDesc': 'DSH upstream is unreachable; open diagnostics', 'hero.pluginDesc': 'The plugin is connected to DSH; start the gateway when needed', 'hero.offlineDesc': 'Start the gateway to accept App and desktop connections', 'hero.openDevices': 'View devices', 'hero.startGateway': 'Start gateway', 'hero.copyToken': 'Copy token',
392
488
  'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower',
393
489
  'theme.panelTitle': 'Choose theme', 'theme.close': 'Close',
394
490
  'repo': 'Repo',
package/public/admin.js CHANGED
@@ -220,6 +220,24 @@ function render(st) {
220
220
  if (cur) cur.textContent = t('gatewayPort.current', { port: gatewayPort })
221
221
  }
222
222
  const upOk = st.upstream.reachable
223
+ const hero = $('admin-hero')
224
+ if (hero) {
225
+ const heroState = isGateway ? (upOk ? 'running' : 'attention') : isPlugin ? 'plugin' : 'offline'
226
+ hero.className = 'admin-hero ' + heroState
227
+ const titleKey = heroState === 'running' ? 'hero.running' : heroState === 'attention' ? 'hero.attention' : heroState === 'plugin' ? 'hero.plugin' : 'hero.offline'
228
+ const descKey = heroState === 'running' ? 'hero.runningDesc' : heroState === 'attention' ? 'hero.attentionDesc' : heroState === 'plugin' ? 'hero.pluginDesc' : 'hero.offlineDesc'
229
+ $('admin-hero-title').textContent = t(titleKey)
230
+ $('admin-hero-desc').textContent = heroState === 'running'
231
+ ? t(descKey, { online: st.onlineCount || 0, requests: st.totalRequests || 0 })
232
+ : t(descKey)
233
+ $('admin-hero-status').textContent = isGateway ? (upOk ? t('stat.reachable') : t('stat.unreachable')) : t(isPlugin ? 'badge.embedded' : 'badge.gatewayDown')
234
+ const action = $('admin-hero-action')
235
+ if (action) {
236
+ const actionKey = heroState === 'plugin' ? 'hero.startGateway' : heroState === 'offline' ? 'hero.copyToken' : 'hero.openDevices'
237
+ action.textContent = t(actionKey)
238
+ action.dataset.heroAction = heroState === 'plugin' ? 'start' : heroState === 'offline' ? 'copy' : 'devices'
239
+ }
240
+ }
223
241
  const hostIPs = (st.lanIPs || []).join(t('stat.ipSep')) || '127.0.0.1'
224
242
  const latestHtml = st.latest?.newer
225
243
  ? `<div class="v">${t('stat.updateAvailable', { version: st.latest.version })}</div><div class="k">${t('stat.currentV', { version: st.version })} · <a href="${st.latest.url || '#'}" target="_blank" rel="noopener" style="color:var(--dsr-accent-strong)">${t('stat.download')}</a></div>`
@@ -388,6 +406,12 @@ $('btn-logout').addEventListener('click', logout)
388
406
  $('btn-close-drawer').addEventListener('click', () => {
389
407
  window.parent.postMessage({ source: 'dsh-remote-admin', type: 'close' }, location.origin)
390
408
  })
409
+ $('admin-hero-action').addEventListener('click', () => {
410
+ const action = $('admin-hero-action').dataset.heroAction
411
+ if (action === 'start') return $('btn-gateway').click()
412
+ if (action === 'copy') return $('btn-copy').click()
413
+ $('device-rows').closest('.table-wrap')?.scrollIntoView({ behavior: 'smooth', block: 'start' })
414
+ })
391
415
  $('btn-copy').addEventListener('click', async () => {
392
416
  try {
393
417
  await navigator.clipboard.writeText(shownToken || token)
@@ -0,0 +1,12 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "id": "2026-08-21-community-test",
5
+ "title": "dsh-Remote 测试公告",
6
+ "content": "这是一条测试公告,如果您已经更新了新版的 dsh-Remote,您将收到这份公告。之后一些内容将会以实时公告的形式发布。\n\n非常遗憾的是,之前在 App 内直接反馈的内容由于当时服务器接收端配置问题,导致没有留下记录。如果您对 dsh-Remote 的后续更新与维护有更好的建议或者反馈,可以通过标题栏中的反馈渠道联系我,我会对反馈内容进行评估后对插件功能进行改善,感谢各位开发者的支持。\n\n同时也非常感谢“品”同学的赞赏,大学生个人开发不易,还请大家多多支持!",
7
+ "minVersion": "0.6.7",
8
+ "maxVersion": "",
9
+ "publishedAt": "2026-08-21T15:00:00+08:00"
10
+ }
11
+ ]
12
+ }