dsh-yunpa 0.10.2 → 0.10.3

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.10.3 — 2026-11-08
4
+ 抽屉从「只能看」变成「能自己走」。
5
+
6
+ - 抽屉加**站点下拉**:选贴吧/NGA/Pixiv/爱丽丝书屋直接跳登录页
7
+ - 抽屉加**地址栏**:输入 URL 回车即导航(自动补 `https://`)
8
+ - 画面取不到时给一行提示,不再静默破图
9
+ - 站点表复用既有的 `/sites` 路由,没为这个下拉新增 host 接口——host 半身改了要重启宿主,能复用就别加
10
+
3
11
  ## 0.10.2 — 2026-11-08
4
12
  发布到 npm 后的两处修正。
5
13
 
package/api.js CHANGED
@@ -116,6 +116,14 @@ export function createApi() {
116
116
  }
117
117
  }
118
118
  if (route === '/live/info') return send(200, { ok: true, ...(await liveInfo()) })
119
+ if (route === '/live/sites') {
120
+ return send(200, {
121
+ ok: true,
122
+ sites: Object.entries(SITES).map(([key, cfg]) => ({
123
+ key, marker: cfg.marker || key, login: cfg.login || '', probe: cfg.probe || '',
124
+ })),
125
+ })
126
+ }
119
127
  if (route === '/live/size' && method === 'POST') {
120
128
  const b = await readBody()
121
129
  const port = await livePort()
package/client.js CHANGED
@@ -38,6 +38,11 @@ window.__ModuleLoader__.load({
38
38
  .yunpa-dock-shot{flex:0 0 auto;overflow:hidden;border-radius:10px;border:.5px solid var(--dsw-alias-border-l2);line-height:0;background:#111}
39
39
  .yunpa-dock-shot img{width:100%;height:auto;display:block;cursor:crosshair}
40
40
  .yunpa-dock-empty{min-height:180px;display:flex;flex-direction:column;gap:10px;align-items:center;justify-content:center;text-align:center;font-size:12px;color:var(--dsw-alias-label-tertiary);padding:0 10px}
41
+ .yunpa-dock-nav{display:flex;gap:6px;align-items:center}
42
+ .yunpa-dock-nav select,.yunpa-dock-nav input{font:inherit;font-size:11px;padding:5px 6px;border-radius:7px;border:1px solid #2e3036;background:#101114;color:#e6e8ee}
43
+ .yunpa-dock-nav select{max-width:98px}
44
+ .yunpa-dock-nav input{flex:1;min-width:0}
45
+ .yunpa-dock-nav input::placeholder{color:#6b6f7a}
41
46
  .yunpa-dock-input{display:flex;gap:6px}
42
47
  .yunpa-dock-input input{flex:1;min-width:0;font:inherit;font-size:12px;padding:6px 8px;border-radius:7px;border:1px solid #2e3036;background:#101114;color:#e6e8ee}
43
48
  .yunpa-dock-input input::placeholder{color:#737783}
@@ -314,6 +319,9 @@ window.__ModuleLoader__.load({
314
319
  const [note, setNote] = useState('')
315
320
  const [text, setText] = useState('')
316
321
  const [frame, setFrame] = useState({ w: 0, h: 0 })
322
+ const [sites, setSites] = useState([])
323
+ const [addr, setAddr] = useState('')
324
+ const [broken, setBroken] = useState(false)
317
325
  const imgRef = React.useRef(null)
318
326
  const shotRef = React.useRef(null)
319
327
 
@@ -446,6 +454,17 @@ window.__ModuleLoader__.load({
446
454
 
447
455
  useEffect(() => { try { localStorage.setItem(DOCK_KEY, open ? '1' : '0') } catch {} }, [open])
448
456
 
457
+ // 站点表:下拉里能直接跳登录页。走既有的 /sites(设置页「站点」页签用的同一份),
458
+ // 不新增 host 路由——host 半身改了要重启,能复用就别加。
459
+ useEffect(() => {
460
+ get('/sites').then((j) => {
461
+ if (!j || !j.ok || !j.sites) return
462
+ setSites(Object.entries(j.sites).map(([key, c]) => ({
463
+ key, marker: (c && c.marker) || key, login: (c && c.login) || '',
464
+ })))
465
+ }).catch(() => {})
466
+ }, [])
467
+
449
468
  // 窗口缩小后把面板拉回可见范围,避免拖到屏幕外找不回来
450
469
  useEffect(() => {
451
470
  const onResize = () => {
@@ -664,13 +683,53 @@ window.__ModuleLoader__.load({
664
683
  h('button', { onPointerDown: stopDrag, onClick: () => setLive((v) => !v), title: live ? '暂停刷新' : '继续刷新' }, live ? '暂停' : '继续'),
665
684
  h('button', { onPointerDown: stopDrag, onClick: () => setOpen(false), title: '收起' }, '收起'),
666
685
  ),
686
+ h('div', { className: 'yunpa-dock-nav' },
687
+ h('select', {
688
+ value: '', title: '跳到该站点登录页',
689
+ onChange: async (e) => {
690
+ const key = e.target.value
691
+ const s = sites.find((x) => x.key === key)
692
+ e.target.value = ''
693
+ if (!s || !s.login) return
694
+ await act('/live/nav', { url: s.login })
695
+ setBroken(false)
696
+ setTick((n) => n + 1)
697
+ },
698
+ },
699
+ h('option', { value: '' }, '站点'),
700
+ sites.map((s) => h('option', { key: s.key, value: s.key }, s.marker)),
701
+ ),
702
+ h('input', {
703
+ value: addr, placeholder: 'https://…(回车跳转)',
704
+ onChange: (e) => setAddr(e.target.value),
705
+ onKeyDown: async (e) => {
706
+ if (e.key !== 'Enter' || !addr.trim()) return
707
+ const url = /^https?:\/\//i.test(addr.trim()) ? addr.trim() : 'https://' + addr.trim()
708
+ await act('/live/nav', { url })
709
+ setAddr('')
710
+ setBroken(false)
711
+ setTick((n) => n + 1)
712
+ },
713
+ }),
714
+ h('button', {
715
+ onClick: async () => {
716
+ if (!addr.trim()) return
717
+ const url = /^https?:\/\//i.test(addr.trim()) ? addr.trim() : 'https://' + addr.trim()
718
+ await act('/live/nav', { url })
719
+ setAddr('')
720
+ setBroken(false)
721
+ setTick((n) => n + 1)
722
+ },
723
+ }, '→'),
724
+ ),
667
725
  running
668
726
  ? h('div', { className: 'yunpa-dock-shot', ref: shotRef },
669
727
  h('img', {
670
728
  ref: imgRef, alt: '受控浏览器画面', draggable: false,
671
729
  src: '/api/yunpa/live.png?q=60&t=' + tick + (frame.w ? '&w=' + frame.w + '&h=' + frame.h : ''),
672
730
  onClick: clickShot,
673
- onError: () => setRunning(false),
731
+ onError: () => setBroken(true),
732
+ onLoad: () => setBroken(false),
674
733
  style: live ? undefined : { opacity: 0.45 },
675
734
  }))
676
735
  : h('div', { className: 'yunpa-dock-empty' },
@@ -698,7 +757,7 @@ window.__ModuleLoader__.load({
698
757
  }, '发送'),
699
758
  h('button', { onClick: () => act('/live/key', { key: 'Enter' }) }, '⏎'),
700
759
  ),
701
- running ? null : null,
760
+ broken ? h('div', { className: 'mono' }, '画面取不到:点「启动」或稍等一帧再试') : null,
702
761
  note && running ? h('div', { className: 'mono' }, note) : null,
703
762
  )
704
763
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-yunpa",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "type": "module",
5
5
  "description": "DSH 插件:本地加密凭据库 + 无头浏览器登录态 + 带登录态抓取(贴吧/NGA/Pixiv/书屋),含浏览器密码迁移与设置页",
6
6
  "main": "./index.js",
@@ -119,6 +119,12 @@ for (let round = 1; round <= ROUNDS; round++) {
119
119
  const wheel = await json('/live/scroll', { method: 'POST', body: JSON.stringify({ dy: 400 }) })
120
120
  rec(round, '滚轮转发', wheel.body?.ok === true, JSON.stringify(wheel.body).slice(0, 60))
121
121
 
122
+ // 4c) 站点表:抽屉里的站点下拉靠它(用既有的 /sites)
123
+ const siteRes = await json('/sites')
124
+ const siteKeys = Object.keys(siteRes.body?.sites || {})
125
+ rec(round, '站点表 /sites', siteRes.body?.ok === true && siteKeys.length > 0,
126
+ siteKeys.join(',') || '空')
127
+
122
128
  // 5) 视口跟随面板尺寸:尺寸随请求参数走,截出来就该是这个大小
123
129
  // (Emulation 覆盖是会话级的,所以每帧都得在同一个会话里重设 —— 这是 /live.size 单一接口做不到的)
124
130
  const target = round % 2 === 0 ? { w: 420, h: 560 } : { w: 480, h: 600 }