dsh-notify-say 0.4.40 → 0.4.44

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/lib/client.js CHANGED
@@ -477,6 +477,9 @@ window.__ModuleLoader__.load({
477
477
  return {
478
478
  loaded: p.loaded, values: p.values, packs: p.packs, advOpen: p.advOpen, busy: p.busy, msg: p.msg, err: p.err,
479
479
  packs: { loading: true, list: null, error: null },
480
+ // ⚠️ 2026-10-18 第三轮审阅:原来 termsAck / termsPending
481
+ // 被写在这里(packs 子对象内层),层级错了 → 刷新列表后
482
+ // 顶层确认状态被清掉,用户被反复要求确认同一音色。
480
483
  termsAck: p.termsAck, termsPending: p.termsPending,
481
484
  }
482
485
  })
@@ -486,11 +489,12 @@ window.__ModuleLoader__.load({
486
489
  return {
487
490
  loaded: p.loaded, values: p.values, packs: p.packs, advOpen: p.advOpen, busy: p.busy, msg: p.msg, err: p.err,
488
491
  packs: {
489
- termsAck: p.termsAck, termsPending: p.termsPending,
490
492
  loading: false,
491
493
  list: ok ? doc.packs : null,
492
494
  error: ok ? null : ((doc && (doc.indexError || doc.reason)) || "无法获取"),
493
495
  },
496
+ // 顶层状态:必须与 packs 平级,不能被塞进 packs 内层
497
+ termsAck: p.termsAck, termsPending: p.termsPending,
494
498
  }
495
499
  })
496
500
  })
@@ -564,6 +568,113 @@ window.__ModuleLoader__.load({
564
568
  // · 过很久后忘了条款内容
565
569
  // 每次都问又很烦。折中:**只在本次会话内记住**。
566
570
  // 浏览器刷新后重新问一次 —— 成本很低(点一下),收益是条款不会被遗忘。
571
+ // ── 条款确认弹窗(v0.4.43)──────────────────────────────────────
572
+ //
573
+ // ## 为什么从「内联面板」改成「居中覆盖」
574
+ //
575
+ // v0.4.40 把确认面板放在语音包列表**下面**。2026-10-18 用户实测反馈:
576
+ // 点列表上面几个音色时,面板在下面看不见 ——
577
+ // 用户以为已经在下载,实际在等一个他没看见的确认。
578
+ //
579
+ // 这不只是「位置靠下」,而是**不阻断**:后面所有按钮照常可点。
580
+ // 覆盖式遮罩能挡住交互,用户必须先处理这个确认。
581
+ //
582
+ // ## 为什么用 position:fixed 而不是 portal
583
+ //
584
+ // 这个渲染环境没有 portal API。fixed 相对视口居中,
585
+ // 不受设置卡片滚动影响。
586
+ //
587
+ // ## 为什么点遮罩就取消
588
+ //
589
+ // 弹窗惯例,也避免用户被「卡住不知道怎么办」。
590
+ // 只有点遮罩本身才关(判断 target === currentTarget),点卡片内部不关。
591
+ function renderTermsModal(tp) {
592
+ return h('div', {
593
+ key: 'terms-backdrop',
594
+ style: {
595
+ position: 'fixed',
596
+ top: 0, right: 0, bottom: 0, left: 0,
597
+ background: 'rgba(0,0,0,.45)',
598
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
599
+ zIndex: 9999,
600
+ },
601
+ onClick: function (e) {
602
+ if (e && e.target === e.currentTarget) cancelTerms()
603
+ },
604
+ },
605
+ h('div', {
606
+ key: 'terms-card',
607
+ style: {
608
+ width: 'min(430px, calc(100vw - 48px))',
609
+ maxHeight: 'calc(100vh - 64px)',
610
+ overflowY: 'auto',
611
+ background: '#2b2b2b',
612
+ color: '#eee',
613
+ border: '1px solid rgba(255,200,80,.5)',
614
+ borderRadius: '8px',
615
+ padding: '16px',
616
+ boxShadow: '0 12px 48px rgba(0,0,0,.55)',
617
+ display: 'flex', flexDirection: 'column', gap: '8px',
618
+ },
619
+ },
620
+ h('div', { style: { fontSize: '15px', fontWeight: 600 } },
621
+ '下载前请先看条款'),
622
+ h('div', { style: { fontSize: '13px', opacity: 0.85 } },
623
+ (tp.name || tp.key) + '(' + tp.key + ')'),
624
+
625
+ // 条款区:只给署名 + 官方链接,不写我们自己转述的摘要(转述会失真)
626
+ h('div', {
627
+ style: {
628
+ marginTop: '4px', padding: '10px',
629
+ background: 'rgba(255,200,80,.10)',
630
+ border: '1px solid rgba(255,200,80,.28)',
631
+ borderRadius: '5px',
632
+ display: 'flex', flexDirection: 'column', gap: '6px',
633
+ fontSize: '12.5px',
634
+ },
635
+ },
636
+ h('div', null,
637
+ tp.credit
638
+ ? ('署名要求:' + tp.credit)
639
+ : h('span', { style: { color: '#e0a030' } }, '该音色未标注署名要求')),
640
+ tp.termsUrl
641
+ ? h('div', null,
642
+ '官方条款:',
643
+ h('a', {
644
+ href: tp.termsUrl,
645
+ target: '_blank',
646
+ rel: 'noreferrer noopener',
647
+ style: { color: '#6cf', wordBreak: 'break-all' },
648
+ }, tp.termsUrl),
649
+ )
650
+ : h('div', { style: { color: '#e0a030' } },
651
+ '该音色没有登记官方条款链接 —— 使用前请自行查证作者条款。'),
652
+ h('div', { style: { opacity: 0.7 } },
653
+ '下载后 zip 内也带 TERMS.md。你用它做的内容(视频、直播等)仍需遵守上述条款。'),
654
+ ),
655
+
656
+ h('div', { style: { opacity: 0.6, fontSize: '11.5px' } },
657
+ '插件已按官方原文审核过该音色可用于本插件;'
658
+ + '这里确认的是「你自己使用这些语音时也要守上述条款」。'),
659
+
660
+ h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: '8px', marginTop: '6px' } },
661
+ h('button', {
662
+ type: 'button',
663
+ style: S.btn,
664
+ onClick: function () { cancelTerms() },
665
+ }, '取消'),
666
+ h('button', {
667
+ type: 'button',
668
+ style: Object.assign({}, S.btn, {
669
+ background: '#3a6', color: '#fff', border: '1px solid #3a6',
670
+ }),
671
+ onClick: function () { ackTerms() },
672
+ }, '我已阅读,开始下载'),
673
+ ),
674
+ ),
675
+ )
676
+ }
677
+
567
678
  function askTerms(pack) {
568
679
  set(function (p) {
569
680
  return {
@@ -876,7 +987,13 @@ window.__ModuleLoader__.load({
876
987
  p.installed ? h('span', { style: { color: '#2a7' } }, '已安装') : null,
877
988
  h('button', {
878
989
  type: 'button',
879
- disabled: s.busy || !p.downloadable,
990
+ // ⚠️ 2026-10-18 第三轮审阅指出:原来的条件是
991
+ // `s.busy || !p.downloadable`,而离线时远程索引拿不到,
992
+ // 本地已装的包会是 installed=true / downloadable=false
993
+ // → **连卸载按钮都被禁用**。
994
+ //
995
+ // 卸载动的是本地文件,不该依赖 GitHub 在线状态。
996
+ disabled: s.busy || (!p.installed && !p.downloadable),
880
997
  style: S.btn,
881
998
  onClick: function () {
882
999
  if (p.installed) { removePack(p.key, p.name || p.key); return }
@@ -904,44 +1021,6 @@ window.__ModuleLoader__.load({
904
1021
  ))
905
1022
  rows.push(h('div', { key: 'vp-hint', style: S.subHint },
906
1023
  '这些语音由 VOICEVOX 生成,各有各的署名与用途限制;下载后会附 TERMS.md。'))
907
- // ── 下载前条款确认面板(v0.4.40)─────────────────────────────
908
- //
909
- // 只在用户点了某个未确认音色的「下载」时出现。
910
- // 内容刻意只放:署名 + 官方条款链接 + 明确的确认动作。
911
- // 不放我们自己转述的条款摘要 —— 转述会失真,用户该看原文。
912
- if (s.termsPending) {
913
- var tp = s.termsPending
914
- rows.push(h('div', {
915
- key: 'vp-terms-box',
916
- style: {
917
- marginTop: '6px', padding: '8px', borderRadius: '4px',
918
- background: 'rgba(255,200,80,.10)',
919
- border: '1px solid rgba(255,200,80,.35)',
920
- display: 'flex', flexDirection: 'column', gap: '4px',
921
- },
922
- },
923
- h('div', { style: { fontWeight: 600 } }, '下载前请先看条款:' + (tp.name || tp.key)),
924
- h('div', { style: S.subHint },
925
- tp.credit ? ('署名要求:' + tp.credit) : '该音色未标注署名要求'),
926
- tp.termsUrl
927
- ? h('div', { style: S.subHint },
928
- '官方条款:',
929
- h('a', {
930
- href: tp.termsUrl,
931
- target: '_blank',
932
- rel: 'noreferrer noopener',
933
- style: { color: '#4a9' },
934
- }, tp.termsUrl),
935
- )
936
- : h('div', { style: S.subHint }, '该音色没有登记官方条款链接 —— 请谨慎使用。'),
937
- h('div', { style: S.subHint },
938
- '下载后 zip 内也带 TERMS.md。你用它做的内容(视频、直播等)仍需遵守上述条款。'),
939
- h('div', { style: S.row },
940
- h('button', { type: 'button', style: S.btn, onClick: function () { ackTerms() } }, '我已阅读,下载'),
941
- h('button', { type: 'button', style: S.btn, onClick: function () { cancelTerms() } }, '取消'),
942
- ),
943
- ))
944
- }
945
1024
  }
946
1025
  }
947
1026
  // ── 语音包专属设置(仅 bundled 时显示)──────────────────────────────
@@ -1642,7 +1721,14 @@ window.__ModuleLoader__.load({
1642
1721
  rows.push(h('div', { key: 'foot', style: S.foot },
1643
1722
  '改动立即生效,无需重启。以上为运行时覆盖,优先级高于 cordis.patch.yml。'))
1644
1723
 
1645
- return h('div', { style: S.box }, rows)
1724
+ // ── 根渲染:设置本体 + 可能出现的确认弹窗(v0.4.43)──────────────
1725
+ //
1726
+ // 弹窗作为**兄弟节点**挂在这里,而不是塞进 rows:
1727
+ // · 塞进 rows 会跟着设置卡片滚动,位置不固定(这正是 v0.4.40 的问题)
1728
+ // · 用 position:fixed 才能稳定居中覆盖
1729
+ var rootKids = [h('div', { key: 'root-box', style: S.box }, rows)]
1730
+ if (s.termsPending) rootKids.push(renderTermsModal(s.termsPending))
1731
+ return h('div', { key: 'root-wrap' }, rootKids)
1646
1732
  }
1647
1733
 
1648
1734
  /**