dsh-bili-watch 0.2.6 → 0.2.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.
Files changed (3) hide show
  1. package/client.js +141 -2
  2. package/index.js +25 -0
  3. package/package.json +1 -1
package/client.js CHANGED
@@ -41,6 +41,11 @@ window.__ModuleLoader__.load({
41
41
  [data-bl-name]{min-width:150px;flex:none;}
42
42
  [data-bl-detail]{flex:1;min-width:0;color:var(--dsw-alias-label-tertiary,#8a9099);font-size:12px;word-break:break-all;}
43
43
  [data-bl-size]{flex:none;color:var(--dsw-alias-label-tertiary,#8a9099);font-size:12px;min-width:64px;text-align:right;}
44
+ [data-bl-overlay-icon]{font-size:14px;line-height:1;color:#d97706}
45
+ [data-bl-overlay-text]{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;overflow:hidden}[data-bl-overlay-line]{display:flex;align-items:baseline;gap:8px;overflow:hidden}[data-bl-overlay-hint]{font-size:11.5px;color:#a16207;opacity:.92;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
46
+ [data-bl-overlay-text b]{font-weight:600;white-space:nowrap}
47
+ [data-bl-overlay-sub]{color:#9a6a1e;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
48
+ [data-bl-overlay-actions]{display:flex;gap:8px;align-items:center;flex-shrink:0}
44
49
  [data-bl-btn]{font-size:12px;min-height:26px;padding:2px 10px;border-radius:6px;cursor:pointer;border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,.16));background:transparent;color:inherit;flex:none;}
45
50
  [data-bl-btn:disabled]{opacity:.45;cursor:default;}
46
51
  [data-bl-input]{width:64px;font-size:12px;padding:2px 6px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,.16));background:transparent;color:inherit;}
@@ -91,6 +96,21 @@ window.__ModuleLoader__.load({
91
96
  )
92
97
  }
93
98
 
99
+ // 设置页入口:先在插件详情页关掉「在设置页显示」时,这里自动隐身
100
+ function SettingsPanel() {
101
+ const [show, setShow] = React.useState(null)
102
+ React.useEffect(() => {
103
+ jget('/status')
104
+ .then((j) => setShow(j.ok && j.data.ui?.showInSettings !== false))
105
+ .catch(() => setShow(false))
106
+ const onUi = (e) => setShow(!!e?.detail?.showInSettings)
107
+ window.addEventListener('bili-watch-ui', onUi)
108
+ return () => window.removeEventListener('bili-watch-ui', onUi)
109
+ }, [])
110
+ if (!show) return null
111
+ return h(Panel, null)
112
+ }
113
+
94
114
  function Panel() {
95
115
  const [st, setSt] = React.useState(null)
96
116
  const [err, setErr] = React.useState('')
@@ -278,6 +298,27 @@ window.__ModuleLoader__.load({
278
298
  const lastRun = st.lastRun || {}
279
299
 
280
300
  return h('div', { 'data-bl-page': '' },
301
+ h('div', { 'data-bl-sec': '' },
302
+ h('div', { 'data-bl-sechead': '' },
303
+ h('span', { 'data-bl-sec-title': '' }, '显示'),
304
+ h('label', { 'data-bl-dlgrow': '' },
305
+ h('input', {
306
+ type: 'checkbox',
307
+ checked: st.ui?.showInSettings !== false,
308
+ onChange: async (e) => {
309
+ const v = e.target.checked
310
+ try {
311
+ await jpost('/config', { showInSettings: v })
312
+ try { window.__biliWatchSyncSettings && window.__biliWatchSyncSettings(v) } catch {}
313
+ await load()
314
+ } catch {}
315
+ },
316
+ }),
317
+ h('span', { 'data-bl-name': '' }, '在设置页也显示'),
318
+ ),
319
+ ),
320
+ h('div', { 'data-bl-note': '' }, '关掉只隐藏设置页入口;插件详情页这块面板一直保留。'),
321
+ ),
281
322
  h('div', { 'data-bl-head': '' },
282
323
  h('span', { 'data-bl-title': '' }, '依赖与状态'),
283
324
  h('span', { 'data-bl-sub': '' }, '缺什么点下载,装好后这里会变绿。')),
@@ -364,22 +405,120 @@ window.__ModuleLoader__.load({
364
405
  )
365
406
  }
366
407
 
408
+ // 缺依赖横幅(shell.overlay)
409
+ function OverlayBanner() {
410
+ const [st, setSt] = React.useState(null)
411
+ const [visible, setVisible] = React.useState(false)
412
+ const [dismissed, setDismissed] = React.useState(false)
413
+
414
+ React.useEffect(() => {
415
+ jget('/status').then((j) => {
416
+ if (!j.ok) return
417
+ setSt(j.data)
418
+ const missing = (j.data.deps || []).filter((d) => !d.ok)
419
+ if (missing.length > 0 && j.data.ui?.hintDismissed !== true) {
420
+ setVisible(true)
421
+ }
422
+ }).catch(() => {})
423
+ }, [])
424
+
425
+ if (!visible || dismissed) return null
426
+ const missing = (st?.deps || []).filter((d) => !d.ok)
427
+ const names = missing.map((d) => d.name).join('、')
428
+
429
+ return h('div', { 'data-bl-overlay': '' },
430
+ h('span', { 'data-bl-overlay-icon': '' }, '⚠'),
431
+ h('span', { 'data-bl-overlay-text': '' },
432
+ h('span', { 'data-bl-overlay-line': '' },
433
+ h('b', {}, 'bili-watch 缺 ' + missing.length + ' 个依赖'),
434
+ h('span', { 'data-bl-overlay-sub': '' }, names + ' — 元数据、评论、弹幕、搜索照常可用')),
435
+ h('span', { 'data-bl-overlay-hint': '' }, '也可以到「设置 → bili-watch」的依赖面板里逐个安装,能看到进度和体积')),
436
+ h('span', { 'data-bl-overlay-actions': '' },
437
+ h('button', { 'data-bl-btn': 'primary', onClick: async () => { await jpost('/download', { target: 'all' }) } }, '去安装'),
438
+ h('button', { 'data-bl-btn': 'ghost', onClick: () => setDismissed(true) }, '关闭'),
439
+ h('button', { 'data-bl-btn': 'ghost', onClick: async () => {
440
+ await jpost('/config', { hintDismissed: true })
441
+ setDismissed(true)
442
+ } }, '不再提示')),
443
+ )
444
+ }
445
+
446
+ // 启用指引(plugins.bundle.activation)
447
+ function ActivationGuide() {
448
+ const [seen, setSeen] = React.useState(false)
449
+ React.useEffect(() => {
450
+ jget('/status').then((j) => {
451
+ if (j.ok && j.data.ui?.activationSeen === true) setSeen(true)
452
+ }).catch(() => {})
453
+ }, [])
454
+ if (seen) return null
455
+ return h('div', { 'data-bl-activation': '' },
456
+ h('div', { 'data-bl-title': '' }, 'B站看视频插件已启用'),
457
+ h('div', { 'data-bl-note': '' }, '用它之前建议先装依赖:ffmpeg(抽帧/取音轨)、转写模型(本地转写)。没装也能用:元数据、评论、弹幕、搜索、热榜。'),
458
+ h('div', { 'data-bl-row': '' },
459
+ h('button', { 'data-bl-btn': 'primary', onClick: async () => { await jpost('/download', { target: 'all' }) } }, '去安装'),
460
+ h('button', { 'data-bl-btn': '', onClick: async () => {
461
+ await jpost('/config', { activationSeen: true })
462
+ setSeen(true)
463
+ } }, '先不装')),
464
+ )
465
+ }
466
+
367
467
  return {
368
468
  inject: ['slots'],
369
469
  apply(ctx) {
370
470
  const style = document.createElement('style')
371
471
  style.setAttribute('data-plugin-css', 'dsh-bili-watch')
372
- style.textContent = CSS
472
+ style.textContent = CSS + `
473
+ [data-bl-overlay]{position:fixed;top:0;left:0;right:0;z-index:9999;display:flex;align-items:center;gap:10px;padding:9px 16px;background:#fffbeb;color:#7c2d12;border-bottom:1px solid #fcd34d;border-left:4px solid #f59e0b;font-size:13px;box-shadow:0 1px 3px rgba(0,0,0,.06);}[data-bl-btn='primary']{background:#f59e0b;border-color:#f59e0b;color:#fff;font-weight:600;}[data-bl-btn='primary']:hover{background:#d97706;}[data-bl-btn='ghost']{border-color:transparent;color:#92400e;}[data-bl-btn='ghost']:hover{background:rgba(146,64,14,.08);}
474
+ [data-bl-overlay-text]{flex:1;}
475
+ [data-bl-activation]{display:flex;flex-direction:column;gap:8px;padding:12px;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.18));border-radius:8px;background:var(--dsw-alias-bg2,rgba(255,255,255,.04));}
476
+ `
373
477
  document.head.appendChild(style)
374
478
  if (typeof ctx.effect === 'function') ctx.effect(() => () => style.remove(), 'dsh-bili-watch: 面板样式')
375
479
 
376
480
  const slots = (typeof ctx.get === 'function' ? ctx.get('slots') : undefined) || ctx.slots
377
481
  if (!slots) return
378
- // 插件详情页的配置区:key 用包名,按包页面 dispatch(见 Slot 清单 plugins.bundle.config)
482
+
483
+ // ① 插件详情页配置区(原有)
379
484
  slots.inject('plugins.bundle.config', () => slots.register(
380
485
  { name: 'plugins.bundle.config', key: PKG },
381
486
  Panel,
382
487
  ))
488
+
489
+ // ② 启用指引(用户从插件列表点「启用」后渲染)
490
+ slots.inject('plugins.bundle.activation', () => slots.register(
491
+ { name: 'plugins.bundle.activation', key: PKG },
492
+ ActivationGuide,
493
+ ))
494
+
495
+ // ③ 缺依赖横幅(全页浮层)
496
+ slots.inject('shell.overlay', () => slots.register(
497
+ { name: 'shell.overlay', id: 'bili-watch.overlay' },
498
+ OverlayBanner,
499
+ ))
500
+
501
+ // ④ 设置页双面板:注册/注销跟着 showInSettings 走(组件返回 null 删不掉左侧 tab)
502
+ let settingsReg = null
503
+ const syncSettings = (on) => {
504
+ if (on) {
505
+ if (settingsReg) return
506
+ slots.inject('settings.section', () => {
507
+ settingsReg = slots.register(
508
+ { name: 'settings.section', id: 'bili-watch', order: 32, label: 'bili-watch' },
509
+ Panel,
510
+ )
511
+ return settingsReg
512
+ })
513
+ } else if (settingsReg) {
514
+ try { settingsReg() } catch {}
515
+ settingsReg = null
516
+ }
517
+ }
518
+ try { window.__biliWatchSyncSettings = syncSettings } catch {}
519
+ jget('/status')
520
+ .then((j) => syncSettings(!!(j.ok && j.data.ui?.showInSettings !== false)))
521
+ .catch(() => {})
383
522
  },
384
523
  }
385
524
  },
package/index.js CHANGED
@@ -45,6 +45,21 @@ const FFMPEG_CANDS = [
45
45
  const HF_HOME = [join(DEPS, '.hf-home'), join(PROJ, '.hf-home')]
46
46
  const SCRIPTS = join(HERE, 'scripts')
47
47
  const CACHE_CFG = join(DEPS, 'cache-config.json')
48
+ const UI_CFG = join(DEPS, 'ui-config.json')
49
+ const DEFAULT_UI_CFG = { hintDismissed: false, activationSeen: false, showInSettings: true }
50
+
51
+ function uiCfg() {
52
+ try {
53
+ const raw = JSON.parse(readFileSync(UI_CFG, 'utf8'))
54
+ return { ...DEFAULT_UI_CFG, ...raw }
55
+ } catch { return { ...DEFAULT_UI_CFG } }
56
+ }
57
+ function writeUiCfg(patch) {
58
+ const cur = uiCfg()
59
+ const next = { ...cur, ...patch }
60
+ writeFileSync(UI_CFG, JSON.stringify(next, null, 2), 'utf8')
61
+ return next
62
+ }
48
63
  const DEFAULT_CACHE_DAYS = 7
49
64
  const DEFAULT_CACHE_MAX_MB = 2048
50
65
  const DOWNLOAD_LOG = join(DEPS, 'download.log')
@@ -674,6 +689,7 @@ function apply(ctx) {
674
689
  deps: depRows(),
675
690
  cache: { ...dirStat(CACHE), ...cacheCfg(), lastAutoClean: cleaned },
676
691
  lastRun: readLastRun(),
692
+ ui: uiCfg(),
677
693
  },
678
694
  })
679
695
  }
@@ -714,6 +730,15 @@ function apply(ctx) {
714
730
  if (r.state === 'ok') writeLastRun('login', 0)
715
731
  return sendJson(res, 200, r)
716
732
  }
733
+ if (req.method === 'POST' && url.pathname === '/api/bili-watch/config') {
734
+ const body = await readJsonBody(req)
735
+ const patch = {}
736
+ if (typeof body?.hintDismissed === 'boolean') patch.hintDismissed = body.hintDismissed
737
+ if (typeof body?.activationSeen === 'boolean') patch.activationSeen = body.activationSeen
738
+ if (typeof body?.showInSettings === 'boolean') patch.showInSettings = body.showInSettings
739
+ const next = writeUiCfg(patch)
740
+ return sendJson(res, 200, { ok: true, data: next })
741
+ }
717
742
  sendJson(res, 404, { ok: false, why: 'not found' })
718
743
  } catch (e) {
719
744
  sendJson(res, 500, { ok: false, why: String(e?.message || e) })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bili-watch",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "private": false,
5
5
  "description": "Reads Bilibili (and generic URLs via yt-dlp) into text for DSH agents: search, hot, metadata, comments, danmaku, AI subtitles, local ASR, frame OCR.",
6
6
  "type": "module",