@sweet-player/core 0.3.0 → 0.4.0

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.md CHANGED
@@ -83,7 +83,11 @@ new SweetPlayer({ ..., plugins: [myPlugin] });
83
83
  // 或运行时:player.use(myPlugin);
84
84
  ```
85
85
 
86
- ### 接入 sweet-subtitle 字幕
86
+ ### 接入 [sweet-subtitle](https://github.com/leuvi/sweet-subtitle) 字幕
87
+
88
+ ```bash
89
+ npm install sweet-subtitle
90
+ ```
87
91
 
88
92
  插件工厂持有字幕实例并对外暴露切换 API,运行时换字幕无需重建播放器:
89
93
 
package/dist/index.cjs CHANGED
@@ -495,69 +495,14 @@ var icons = {
495
495
  ),
496
496
  audio: createSvgIcon(
497
497
  "M12 3a9 9 0 0 0-9 9v7c0 1.1.9 2 2 2h4v-8H5v-1c0-3.87 3.13-7 7-7s7 3.13 7 7v1h-4v8h4c1.1 0 2-.9 2-2v-7a9 9 0 0 0-9-9z"
498
- )
498
+ ),
499
+ settings: createSvgIcon(
500
+ "M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65a.5.5 0 0 0 .12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.49.49 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.49.49 0 0 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65a.5.5 0 0 0-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46a.5.5 0 0 0-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"
501
+ ),
502
+ chevronRight: createSvgIcon("M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"),
503
+ chevronLeft: createSvgIcon("M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z")
499
504
  };
500
505
 
501
- // src/ui/components/menu.ts
502
- function createPopupMenu(opts) {
503
- const root = createEl("div", { className: "sp-menu" });
504
- const btn = createEl("button", {
505
- className: "sp-btn sp-menu-btn",
506
- html: opts.buttonHtml,
507
- attrs: { type: "button", title: opts.title, "aria-label": opts.title },
508
- parent: root
509
- });
510
- const panel = createEl("div", { className: "sp-menu-panel", parent: root });
511
- let items = [];
512
- let activeValue;
513
- function render() {
514
- panel.innerHTML = "";
515
- if (items.length === 0) {
516
- createEl("div", { className: "sp-menu-empty", text: opts.emptyText ?? "-", parent: panel });
517
- return;
518
- }
519
- for (const item of items) {
520
- const itemBtn = createEl("button", {
521
- className: "sp-menu-item" + (item.value === activeValue ? " sp-active" : ""),
522
- text: item.label,
523
- attrs: { type: "button" },
524
- parent: panel
525
- });
526
- itemBtn.addEventListener("click", (e) => {
527
- e.stopPropagation();
528
- close();
529
- opts.onSelect(item);
530
- });
531
- }
532
- }
533
- function close() {
534
- root.classList.remove("sp-open");
535
- }
536
- btn.addEventListener("click", (e) => {
537
- e.stopPropagation();
538
- const willOpen = !root.classList.contains("sp-open");
539
- root.closest(".sp-controls")?.querySelectorAll(".sp-menu.sp-open").forEach((m) => m.classList.remove("sp-open"));
540
- root.classList.toggle("sp-open", willOpen);
541
- });
542
- document.addEventListener("click", close);
543
- render();
544
- return {
545
- el: root,
546
- setItems(next) {
547
- items = next;
548
- render();
549
- },
550
- setActive(value) {
551
- activeValue = value;
552
- render();
553
- },
554
- setButtonContent(html) {
555
- btn.innerHTML = html;
556
- },
557
- close
558
- };
559
- }
560
-
561
506
  // src/ui/components/progressBar.ts
562
507
  function createProgressBar(video, onSeek) {
563
508
  const root = createEl("div", { className: "sp-progress" });
@@ -665,6 +610,157 @@ function createVolumeControl(opts) {
665
610
  };
666
611
  }
667
612
 
613
+ // src/ui/components/settingsPanel.ts
614
+ function createSettingsPanel(opts) {
615
+ const root = createEl("div", { className: "sp-menu sp-settings" });
616
+ const btn = createEl("button", {
617
+ className: "sp-btn",
618
+ html: icons.settings,
619
+ attrs: { type: "button", title: opts.buttonTitle, "aria-label": opts.buttonTitle },
620
+ parent: root
621
+ });
622
+ const panel = createEl("div", { className: "sp-settings-panel", parent: root });
623
+ const mainView = createEl("div", { className: "sp-settings-main", parent: panel });
624
+ const subView = createEl("div", { className: "sp-settings-sub", parent: panel });
625
+ let sections = opts.sections;
626
+ let activeSub = null;
627
+ function renderMain() {
628
+ mainView.innerHTML = "";
629
+ for (const sec of sections) {
630
+ if (sec.toggle) {
631
+ const row2 = createEl("div", {
632
+ className: "sp-settings-row sp-settings-toggle-row",
633
+ attrs: { "data-key": sec.key },
634
+ parent: mainView
635
+ });
636
+ createEl("span", { className: "sp-settings-label", text: sec.label, parent: row2 });
637
+ const track = createEl("div", {
638
+ className: "sp-toggle" + (sec.toggle.checked ? " sp-on" : ""),
639
+ attrs: { role: "switch", "aria-checked": String(sec.toggle.checked), tabindex: "0" },
640
+ parent: row2
641
+ });
642
+ createEl("span", { className: "sp-toggle-thumb", parent: track });
643
+ const handler = (e) => {
644
+ e.stopPropagation();
645
+ sec.toggle.onToggle();
646
+ };
647
+ row2.addEventListener("click", handler);
648
+ track.addEventListener("keydown", (e) => {
649
+ if (e.key === "Enter" || e.key === " ") {
650
+ e.preventDefault();
651
+ handler(e);
652
+ }
653
+ });
654
+ continue;
655
+ }
656
+ if (sec.items.length === 0) continue;
657
+ const row = createEl("button", {
658
+ className: "sp-settings-row",
659
+ attrs: { type: "button", "data-key": sec.key },
660
+ parent: mainView
661
+ });
662
+ createEl("span", { className: "sp-settings-label", text: sec.label, parent: row });
663
+ createEl("span", { className: "sp-settings-value", text: sec.currentValue, parent: row });
664
+ const arrow = createEl("span", { className: "sp-settings-arrow", parent: row });
665
+ arrow.innerHTML = icons.chevronRight;
666
+ row.addEventListener("click", (e) => {
667
+ e.stopPropagation();
668
+ showSub(sec.key);
669
+ });
670
+ }
671
+ }
672
+ function renderSub(key) {
673
+ subView.innerHTML = "";
674
+ const sec = sections.find((s) => s.key === key);
675
+ if (!sec) return;
676
+ const back = createEl("button", {
677
+ className: "sp-settings-back",
678
+ attrs: { type: "button" },
679
+ parent: subView
680
+ });
681
+ const backArrow = createEl("span", { className: "sp-settings-back-arrow", parent: back });
682
+ backArrow.innerHTML = icons.chevronLeft;
683
+ createEl("span", { text: sec.label, parent: back });
684
+ back.addEventListener("click", (e) => {
685
+ e.stopPropagation();
686
+ showMain();
687
+ });
688
+ for (const item of sec.items) {
689
+ const itemBtn = createEl("button", {
690
+ className: "sp-menu-item" + (item.value === sec.activeValue ? " sp-active" : ""),
691
+ text: item.label,
692
+ attrs: { type: "button" },
693
+ parent: subView
694
+ });
695
+ itemBtn.addEventListener("click", (e) => {
696
+ e.stopPropagation();
697
+ close();
698
+ sec.onSelect(item);
699
+ });
700
+ }
701
+ }
702
+ function showSub(key) {
703
+ activeSub = key;
704
+ renderSub(key);
705
+ mainView.style.display = "none";
706
+ subView.style.display = "";
707
+ }
708
+ function showMain() {
709
+ activeSub = null;
710
+ mainView.style.display = "";
711
+ subView.style.display = "none";
712
+ }
713
+ function close() {
714
+ root.classList.remove("sp-open");
715
+ showMain();
716
+ }
717
+ btn.addEventListener("click", (e) => {
718
+ e.stopPropagation();
719
+ const willOpen = !root.classList.contains("sp-open");
720
+ root.closest(".sp-controls")?.querySelectorAll(".sp-menu.sp-open").forEach((m) => m.classList.remove("sp-open"));
721
+ if (willOpen) {
722
+ renderMain();
723
+ showMain();
724
+ }
725
+ root.classList.toggle("sp-open", willOpen);
726
+ });
727
+ const onDocClick = () => close();
728
+ document.addEventListener("click", onDocClick);
729
+ panel.addEventListener("click", (e) => e.stopPropagation());
730
+ renderMain();
731
+ showMain();
732
+ return {
733
+ el: root,
734
+ updateSection(key, patch) {
735
+ const sec = sections.find((s) => s.key === key);
736
+ if (!sec) return;
737
+ if (patch.items !== void 0) sec.items = patch.items;
738
+ if (patch.currentValue !== void 0) sec.currentValue = patch.currentValue;
739
+ if (patch.activeValue !== void 0) sec.activeValue = patch.activeValue;
740
+ if (patch.toggle) {
741
+ if (sec.toggle) {
742
+ sec.toggle.checked = patch.toggle.checked;
743
+ if (patch.toggle.onToggle) sec.toggle.onToggle = patch.toggle.onToggle;
744
+ } else {
745
+ sec.toggle = { checked: patch.toggle.checked, onToggle: patch.toggle.onToggle ?? (() => {
746
+ }) };
747
+ }
748
+ }
749
+ if (sec.toggle) {
750
+ if (!activeSub) renderMain();
751
+ return;
752
+ }
753
+ if (activeSub === key) renderSub(key);
754
+ if (!activeSub) renderMain();
755
+ },
756
+ close,
757
+ destroy() {
758
+ document.removeEventListener("click", onDocClick);
759
+ root.remove();
760
+ }
761
+ };
762
+ }
763
+
668
764
  // src/ui/controls.ts
669
765
  function createControls(ctx) {
670
766
  const { video, actions, i18n, hidden } = ctx;
@@ -699,57 +795,114 @@ function createControls(ctx) {
699
795
  parent: show("time") ? row : void 0
700
796
  });
701
797
  createEl("div", { className: "sp-controls-spacer", parent: row });
702
- const rateMenu = createPopupMenu({
703
- buttonHtml: "1x",
704
- title: i18n.t("speed"),
705
- emptyText: i18n.t("empty"),
706
- onSelect: (item) => actions.setRate(item.value)
707
- });
708
- rateMenu.setItems(ctx.playbackRates.map((r) => ({ label: `${r}x`, value: r })));
709
- rateMenu.setActive(video.playbackRate);
710
- if (show("rate")) row.appendChild(rateMenu.el);
711
- const qualityMenu = createPopupMenu({
712
- buttonHtml: i18n.t("quality"),
713
- title: i18n.t("quality"),
714
- emptyText: i18n.t("empty"),
715
- onSelect: (item) => actions.selectQuality(item.value)
716
- });
717
- if (show("quality")) row.appendChild(qualityMenu.el);
718
- const ratioMenu = createPopupMenu({
719
- buttonHtml: i18n.t("aspectRatio"),
720
- title: i18n.t("aspectRatio"),
721
- emptyText: i18n.t("empty"),
722
- onSelect: (item) => actions.setAspectRatio(item.value)
723
- });
724
- ratioMenu.setItems(
725
- ctx.aspectRatios.map((r) => ({ label: r === "original" ? i18n.t("ratioOriginal") : r, value: r }))
726
- );
727
- ratioMenu.setActive("original");
728
- if (show("ratio")) row.appendChild(ratioMenu.el);
729
- const audioMenu = createPopupMenu({
730
- buttonHtml: icons.audio,
731
- title: i18n.t("audioTrack"),
732
- emptyText: i18n.t("empty"),
733
- onSelect: (item) => actions.selectAudioTrack(item.value)
734
- });
735
- if (show("audioTrack")) row.appendChild(audioMenu.el);
736
798
  const volume = createVolumeControl({
737
799
  muteTitle: i18n.t("mute"),
738
800
  onVolumeChange: actions.setVolume,
739
801
  onToggleMute: actions.toggleMute
740
802
  });
741
803
  if (show("volume")) row.appendChild(volume.el);
742
- if (show("pip") && document.pictureInPictureEnabled) {
743
- button(icons.pip, i18n.t("pip"), actions.togglePip);
744
- }
804
+ let currentRate = video.playbackRate;
805
+ let qualityItems = [];
806
+ let activeQuality;
807
+ let audioItems = [];
808
+ let activeAudio;
809
+ const settingsPanel = createSettingsPanel({
810
+ buttonTitle: i18n.t("settings"),
811
+ sections: [
812
+ ...show("rate") ? [{
813
+ key: "rate",
814
+ label: i18n.t("speed"),
815
+ currentValue: `${currentRate}x`,
816
+ items: ctx.playbackRates.map((r) => ({ label: `${r}x`, value: r })),
817
+ activeValue: currentRate,
818
+ onSelect: (item) => actions.setRate(item.value)
819
+ }] : [],
820
+ ...show("quality") ? [{
821
+ key: "quality",
822
+ label: i18n.t("quality"),
823
+ currentValue: i18n.t("qualityAuto"),
824
+ items: qualityItems,
825
+ activeValue: activeQuality,
826
+ onSelect: (item) => actions.selectQuality(item.value)
827
+ }] : [],
828
+ ...show("ratio") ? [{
829
+ key: "ratio",
830
+ label: i18n.t("aspectRatio"),
831
+ currentValue: i18n.t("ratioOriginal"),
832
+ items: ctx.aspectRatios.map((r) => ({
833
+ label: r === "original" ? i18n.t("ratioOriginal") : r,
834
+ value: r
835
+ })),
836
+ activeValue: "original",
837
+ onSelect: (item) => actions.setAspectRatio(item.value)
838
+ }] : [],
839
+ ...show("audioTrack") ? [{
840
+ key: "audioTrack",
841
+ label: i18n.t("audioTrack"),
842
+ currentValue: "-",
843
+ items: audioItems,
844
+ activeValue: activeAudio,
845
+ onSelect: (item) => actions.selectAudioTrack(item.value)
846
+ }] : [],
847
+ ...show("pip") && document.pictureInPictureEnabled ? [{
848
+ key: "pip",
849
+ label: i18n.t("pip"),
850
+ currentValue: "",
851
+ items: [],
852
+ activeValue: void 0,
853
+ onSelect: () => {
854
+ },
855
+ toggle: {
856
+ checked: false,
857
+ onToggle: () => actions.togglePip()
858
+ }
859
+ }] : []
860
+ ]
861
+ });
862
+ if (show("settings")) row.appendChild(settingsPanel.el);
745
863
  const fsBtn = button(icons.fullscreen, i18n.t("fullscreen"), actions.toggleFullscreen, false, show("fullscreen"));
864
+ const qualityMenuAdapter = {
865
+ el: document.createElement("div"),
866
+ setItems(items) {
867
+ qualityItems = items;
868
+ const cur = activeQuality ? items.find((i) => i.value === activeQuality)?.label : i18n.t("qualityAuto");
869
+ settingsPanel.updateSection("quality", { items, currentValue: cur ?? i18n.t("qualityAuto") });
870
+ },
871
+ setActive(value) {
872
+ activeQuality = value;
873
+ const label = qualityItems.find((i) => i.value === value)?.label ?? i18n.t("qualityAuto");
874
+ settingsPanel.updateSection("quality", { activeValue: value, currentValue: label });
875
+ },
876
+ setButtonContent() {
877
+ },
878
+ close() {
879
+ }
880
+ };
881
+ const audioMenuAdapter = {
882
+ el: document.createElement("div"),
883
+ setItems(items) {
884
+ audioItems = items;
885
+ const cur = activeAudio ? items.find((i) => i.value === activeAudio)?.label : "-";
886
+ settingsPanel.updateSection("audioTrack", { items, currentValue: cur ?? "-" });
887
+ },
888
+ setActive(value) {
889
+ activeAudio = value;
890
+ const label = audioItems.find((i) => i.value === value)?.label ?? "-";
891
+ settingsPanel.updateSection("audioTrack", { activeValue: value, currentValue: label });
892
+ },
893
+ setButtonContent() {
894
+ },
895
+ close() {
896
+ }
897
+ };
746
898
  return {
747
899
  topEl,
748
900
  bottomEl,
749
901
  progress,
750
902
  volume,
751
- qualityMenu,
752
- audioMenu,
903
+ qualityMenu: qualityMenuAdapter,
904
+ audioMenu: audioMenuAdapter,
905
+ settingsPanel,
753
906
  updatePlayState(playing) {
754
907
  playBtn.innerHTML = playing ? icons.pause : icons.play;
755
908
  },
@@ -758,16 +911,21 @@ function createControls(ctx) {
758
911
  progress.update();
759
912
  },
760
913
  updateRate(rate) {
761
- rateMenu.setButtonContent(`${rate}x`);
762
- rateMenu.setActive(rate);
914
+ currentRate = rate;
915
+ settingsPanel.updateSection("rate", { activeValue: rate, currentValue: `${rate}x` });
763
916
  },
764
917
  updateFullscreen(fs) {
765
918
  fsBtn.innerHTML = fs ? icons.fullscreenExit : icons.fullscreen;
766
919
  },
920
+ updatePip(pip) {
921
+ settingsPanel.updateSection("pip", { toggle: { checked: pip } });
922
+ },
767
923
  updateRatio(ratio) {
768
- ratioMenu.setActive(ratio);
924
+ const label = ratio === "original" ? i18n.t("ratioOriginal") : ratio;
925
+ settingsPanel.updateSection("ratio", { activeValue: ratio, currentValue: label });
769
926
  },
770
927
  destroy() {
928
+ settingsPanel.destroy();
771
929
  topEl.remove();
772
930
  bottomEl.remove();
773
931
  }
@@ -1128,6 +1286,7 @@ var zhCN = {
1128
1286
  scVolume: "\u97F3\u91CF \xB15",
1129
1287
  scFullscreen: "\u5168\u5C4F\u5207\u6362",
1130
1288
  scMute: "\u9759\u97F3\u5207\u6362",
1289
+ settings: "\u8BBE\u7F6E",
1131
1290
  copyLog: "\u590D\u5236\u65E5\u5FD7",
1132
1291
  logCopied: "\u5DF2\u590D\u5236"
1133
1292
  };
@@ -1171,6 +1330,7 @@ var en = {
1171
1330
  scVolume: "Volume \xB15",
1172
1331
  scFullscreen: "Toggle fullscreen",
1173
1332
  scMute: "Toggle mute",
1333
+ settings: "Settings",
1174
1334
  copyLog: "Copy log",
1175
1335
  logCopied: "Copied"
1176
1336
  };
@@ -1246,10 +1406,10 @@ function clearProgress(id) {
1246
1406
  }
1247
1407
 
1248
1408
  // src/version.ts
1249
- var VERSION = true ? "0.3.0" : "dev";
1409
+ var VERSION = true ? "0.4.0" : "dev";
1250
1410
 
1251
1411
  // src/styles/player.css
1252
- var player_default = ".sweet-player {\n --sp-accent: #ff4d6d;\n --sp-bg: #000;\n --sp-text: #fff;\n --sp-bar-height: 4px;\n --sp-bar-hover-height: 6px;\n --sp-control-size: 36px;\n --sp-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--sp-bg);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 13px;\n overflow: hidden;\n user-select: none;\n -webkit-user-select: none;\n outline: none;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sweet-player *,\n.sweet-player *::before,\n.sweet-player *::after {\n box-sizing: border-box;\n}\n\n.sp-video {\n width: 100%;\n height: 100%;\n object-fit: contain;\n display: block;\n}\n\n/* \u5F3A\u5236\u753B\u9762\u6BD4\u4F8B\uFF1Avideo \u5C45\u4E2D\u3001\u6309\u6BD4\u4F8B\u9650\u5236\u5C3A\u5BF8\u5E76\u62C9\u4F38\u586B\u5145 */\n.sweet-player[data-ratio]:not([data-ratio='original']) .sp-video {\n width: auto;\n height: auto;\n max-width: 100%;\n max-height: 100%;\n aspect-ratio: var(--sp-forced-ratio);\n object-fit: fill;\n}\n\n/* ---------- \u9876\u90E8\u6807\u9898\u533A ---------- */\n.sp-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 12px 16px 28px;\n background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);\n transition: opacity 0.25s ease;\n pointer-events: none;\n}\n\n.sp-title {\n display: inline-block;\n max-width: 70%;\n font-size: 15px;\n font-weight: 500;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: auto;\n cursor: default;\n}\n\n/* ---------- \u7EDF\u8BA1\u4FE1\u606F\u8499\u5C42 ---------- */\n.sp-stats {\n position: absolute;\n top: 12px;\n left: 12px;\n z-index: 30;\n min-width: 300px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 6px;\n font-size: 12px;\n line-height: 1.7;\n font-family: 'SFMono-Regular', Consolas, monospace;\n pointer-events: auto;\n}\n\n.sp-stats-header {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 4px;\n margin: -4px -6px 4px 0;\n}\n\n.sp-stats-copy {\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 12px;\n line-height: 22px;\n padding: 0 6px;\n border-radius: 3px;\n white-space: nowrap;\n}\n\n.sp-stats-copy:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-stats-close {\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.sp-stats-close:hover {\n opacity: 1;\n}\n\n.sp-stats-row {\n display: flex;\n gap: 12px;\n}\n\n.sp-stats-row dt {\n flex: 0 0 110px;\n opacity: 0.65;\n margin: 0;\n}\n\n.sp-stats-row dd {\n margin: 0;\n word-break: break-all;\n}\n\n/* ---------- \u5FEB\u6377\u952E\u9762\u677F ---------- */\n.sp-shortcuts {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 30;\n min-width: 280px;\n max-width: calc(100% - 24px);\n max-height: calc(100% - 24px);\n overflow-y: auto;\n padding: 14px 20px 14px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 8px;\n font-size: 13px;\n line-height: 2.1;\n pointer-events: auto;\n}\n\n.sp-shortcuts-title {\n font-size: 14px;\n font-weight: 600;\n margin-bottom: 6px;\n}\n\n.sp-shortcuts-body {\n margin: 0;\n}\n\n.sp-shortcuts-row {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.sp-shortcuts-row dt {\n flex: 0 0 100px;\n margin: 0;\n}\n\n.sp-shortcuts-row dd {\n margin: 0;\n opacity: 0.85;\n}\n\n.sp-kbd {\n display: inline-block;\n padding: 0 7px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 4px;\n font-family: 'SFMono-Regular', Consolas, monospace;\n font-size: 12px;\n line-height: 20px;\n white-space: nowrap;\n}\n\n.sp-shortcuts-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n}\n\n.sp-shortcuts-close:hover {\n opacity: 1;\n}\n\n/* ---------- \u53F3\u952E\u83DC\u5355 ---------- */\n.sp-context-menu {\n position: absolute;\n z-index: 40;\n min-width: 150px;\n padding: 4px 0;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n display: none;\n}\n\n.sp-context-menu.sp-visible {\n display: block;\n}\n\n.sp-context-item {\n display: block;\n width: 100%;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: left;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-context-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n/* ---------- \u4E2D\u592E OSD \u53CD\u9988 ---------- */\n.sp-osd {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 20;\n padding: 10px 18px;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n opacity: 0;\n transition: opacity 0.15s ease;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-osd.sp-visible {\n opacity: 1;\n}\n\n/* ---------- \u5E95\u90E8\u63A7\u5236\u533A ---------- */\n.sp-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 24px 12px 6px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);\n transition: opacity 0.25s ease;\n z-index: 10;\n}\n\n.sweet-player.sp-controls-hidden .sp-bottom,\n.sweet-player.sp-controls-hidden .sp-top {\n opacity: 0;\n pointer-events: none;\n}\n\n.sweet-player.sp-controls-hidden {\n cursor: none;\n}\n\n/* ---------- \u8FDB\u5EA6\u6761 ---------- */\n.sp-progress {\n position: relative;\n height: 14px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-bottom: 4px;\n}\n\n.sp-progress-track {\n position: relative;\n width: 100%;\n height: var(--sp-bar-height);\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n overflow: visible;\n transition: height 0.1s ease;\n}\n\n.sp-progress:hover .sp-progress-track {\n height: var(--sp-bar-hover-height);\n}\n\n.sp-progress-buffered,\n.sp-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n}\n\n.sp-progress-buffered {\n background: rgba(255, 255, 255, 0.35);\n}\n\n.sp-progress-played {\n background: var(--sp-accent);\n}\n\n.sp-progress-thumb {\n position: absolute;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--sp-accent);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.1s ease;\n pointer-events: none;\n}\n\n.sp-progress:hover .sp-progress-thumb,\n.sp-progress.sp-dragging .sp-progress-thumb {\n transform: translate(-50%, -50%) scale(1);\n}\n\n.sp-progress-tooltip {\n position: absolute;\n bottom: 18px;\n transform: translateX(-50%);\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 3px;\n font-size: 12px;\n display: none;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-progress:hover .sp-progress-tooltip {\n display: block;\n}\n\n/* ---------- \u63A7\u5236\u6309\u94AE\u884C ---------- */\n.sp-controls {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.sp-controls-spacer {\n flex: 1;\n}\n\n.sp-btn {\n width: var(--sp-control-size);\n height: var(--sp-control-size);\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0;\n opacity: 0.9;\n transition: opacity 0.1s ease, background 0.1s ease;\n}\n\n.sp-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.sp-btn:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.sp-time {\n padding: 0 8px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n opacity: 0.95;\n}\n\n/* ---------- \u5F39\u51FA\u83DC\u5355 ---------- */\n.sp-menu {\n position: relative;\n}\n\n.sp-menu-btn {\n min-width: var(--sp-control-size);\n width: auto;\n padding: 0 8px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.sp-menu-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n min-width: 80px;\n max-height: 220px;\n overflow-y: auto;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-menu.sp-open .sp-menu-panel {\n display: block;\n}\n\n.sp-menu-item {\n display: block;\n width: 100%;\n padding: 7px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: center;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-menu-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-menu-item.sp-active {\n color: var(--sp-accent);\n font-weight: 600;\n}\n\n.sp-menu-empty {\n padding: 7px 16px;\n opacity: 0.5;\n white-space: nowrap;\n}\n\n/* ---------- \u72B6\u6001\u8499\u5C42\uFF1Aloading / \u9519\u8BEF / \u7ED3\u675F ---------- */\n.sp-state {\n position: absolute;\n inset: 0;\n z-index: 15;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.sp-spinner {\n opacity: 0;\n transition: opacity 0.2s ease 0.3s; /* \u5EF6\u8FDF\u51FA\u73B0\uFF0C\u907F\u514D\u77ED\u6682 seek \u95EA\u70C1 */\n}\n\n.sp-state.sp-loading .sp-spinner {\n opacity: 1;\n}\n\n.sp-spinner-ring {\n width: 44px;\n height: 44px;\n border: 4px solid rgba(255, 255, 255, 0.25);\n border-top-color: var(--sp-accent);\n border-radius: 50%;\n animation: sp-spin 0.8s linear infinite;\n}\n\n@keyframes sp-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sp-state-panel {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 14px;\n background: rgba(0, 0, 0, 0.55);\n pointer-events: auto;\n}\n\n.sp-state-message {\n font-size: 15px;\n opacity: 0.95;\n}\n\n.sp-state-actions {\n display: flex;\n gap: 12px;\n}\n\n.sp-state-btn {\n padding: 8px 22px;\n border: 1px solid rgba(255, 255, 255, 0.5);\n border-radius: 18px;\n background: transparent;\n color: var(--sp-text);\n font-size: 14px;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.sp-state-btn:hover {\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-state-btn-primary {\n background: var(--sp-accent);\n border-color: var(--sp-accent);\n}\n\n.sp-state-btn-primary:hover {\n background: var(--sp-accent);\n filter: brightness(1.1);\n}\n\n.sp-state-countdown {\n font-size: 13px;\n opacity: 0.85;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.sp-state-cancel {\n border: none;\n background: transparent;\n color: var(--sp-accent);\n cursor: pointer;\n font-size: 13px;\n padding: 2px 6px;\n}\n\n/* ---------- \u97F3\u91CF ---------- */\n.sp-volume {\n display: flex;\n align-items: center;\n}\n\n.sp-volume-slider {\n width: 0;\n overflow: hidden;\n transition: width 0.15s ease;\n display: flex;\n align-items: center;\n}\n\n.sp-volume:hover .sp-volume-slider,\n.sp-volume.sp-dragging .sp-volume-slider {\n width: 64px;\n}\n\n.sp-volume-track {\n position: relative;\n width: 56px;\n height: var(--sp-bar-height);\n margin: 0 4px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.sp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-text);\n border-radius: 2px;\n}\n\n.sp-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--sp-text);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n";
1412
+ var player_default = ".sweet-player {\n --sp-accent: #ff4d6d;\n --sp-bg: #000;\n --sp-text: #fff;\n --sp-bar-height: 4px;\n --sp-bar-hover-height: 6px;\n --sp-control-size: 36px;\n --sp-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--sp-bg);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 13px;\n overflow: hidden;\n user-select: none;\n -webkit-user-select: none;\n outline: none;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sweet-player *,\n.sweet-player *::before,\n.sweet-player *::after {\n box-sizing: border-box;\n}\n\n.sp-video {\n width: 100%;\n height: 100%;\n object-fit: contain;\n display: block;\n}\n\n/* \u5F3A\u5236\u753B\u9762\u6BD4\u4F8B\uFF1Avideo \u5C45\u4E2D\u3001\u6309\u6BD4\u4F8B\u9650\u5236\u5C3A\u5BF8\u5E76\u62C9\u4F38\u586B\u5145 */\n.sweet-player[data-ratio]:not([data-ratio='original']) .sp-video {\n width: auto;\n height: auto;\n max-width: 100%;\n max-height: 100%;\n aspect-ratio: var(--sp-forced-ratio);\n object-fit: fill;\n}\n\n/* ---------- \u9876\u90E8\u6807\u9898\u533A ---------- */\n.sp-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 12px 16px 28px;\n background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);\n transition: opacity 0.25s ease;\n pointer-events: none;\n}\n\n.sp-title {\n display: inline-block;\n max-width: 70%;\n font-size: 15px;\n font-weight: 500;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: auto;\n cursor: default;\n}\n\n/* ---------- \u7EDF\u8BA1\u4FE1\u606F\u8499\u5C42 ---------- */\n.sp-stats {\n position: absolute;\n top: 12px;\n left: 12px;\n z-index: 30;\n min-width: 300px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 6px;\n font-size: 12px;\n line-height: 1.7;\n font-family: 'SFMono-Regular', Consolas, monospace;\n pointer-events: auto;\n}\n\n.sp-stats-header {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 4px;\n margin: -4px -6px 4px 0;\n}\n\n.sp-stats-copy {\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 12px;\n line-height: 22px;\n padding: 0 6px;\n border-radius: 3px;\n white-space: nowrap;\n}\n\n.sp-stats-copy:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-stats-close {\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.sp-stats-close:hover {\n opacity: 1;\n}\n\n.sp-stats-row {\n display: flex;\n gap: 12px;\n}\n\n.sp-stats-row dt {\n flex: 0 0 110px;\n opacity: 0.65;\n margin: 0;\n}\n\n.sp-stats-row dd {\n margin: 0;\n word-break: break-all;\n}\n\n/* ---------- \u5FEB\u6377\u952E\u9762\u677F ---------- */\n.sp-shortcuts {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 30;\n min-width: 280px;\n max-width: calc(100% - 24px);\n max-height: calc(100% - 24px);\n overflow-y: auto;\n padding: 14px 20px 14px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 8px;\n font-size: 13px;\n line-height: 2.1;\n pointer-events: auto;\n}\n\n.sp-shortcuts-title {\n font-size: 14px;\n font-weight: 600;\n margin-bottom: 6px;\n}\n\n.sp-shortcuts-body {\n margin: 0;\n}\n\n.sp-shortcuts-row {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.sp-shortcuts-row dt {\n flex: 0 0 100px;\n margin: 0;\n}\n\n.sp-shortcuts-row dd {\n margin: 0;\n opacity: 0.85;\n}\n\n.sp-kbd {\n display: inline-block;\n padding: 0 7px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 4px;\n font-family: 'SFMono-Regular', Consolas, monospace;\n font-size: 12px;\n line-height: 20px;\n white-space: nowrap;\n}\n\n.sp-shortcuts-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n}\n\n.sp-shortcuts-close:hover {\n opacity: 1;\n}\n\n/* ---------- \u53F3\u952E\u83DC\u5355 ---------- */\n.sp-context-menu {\n position: absolute;\n z-index: 40;\n min-width: 150px;\n padding: 4px 0;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n display: none;\n}\n\n.sp-context-menu.sp-visible {\n display: block;\n}\n\n.sp-context-item {\n display: block;\n width: 100%;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: left;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-context-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n/* ---------- \u4E2D\u592E OSD \u53CD\u9988 ---------- */\n.sp-osd {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 20;\n padding: 10px 18px;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n opacity: 0;\n transition: opacity 0.15s ease;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-osd.sp-visible {\n opacity: 1;\n}\n\n/* ---------- \u5E95\u90E8\u63A7\u5236\u533A ---------- */\n.sp-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 24px 12px 6px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);\n transition: opacity 0.25s ease;\n z-index: 10;\n}\n\n.sweet-player.sp-controls-hidden .sp-bottom,\n.sweet-player.sp-controls-hidden .sp-top {\n opacity: 0;\n pointer-events: none;\n}\n\n.sweet-player.sp-controls-hidden {\n cursor: none;\n}\n\n/* ---------- \u8FDB\u5EA6\u6761 ---------- */\n.sp-progress {\n position: relative;\n height: 14px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-bottom: 4px;\n}\n\n.sp-progress-track {\n position: relative;\n width: 100%;\n height: var(--sp-bar-height);\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n overflow: visible;\n transition: height 0.1s ease;\n}\n\n.sp-progress:hover .sp-progress-track {\n height: var(--sp-bar-hover-height);\n}\n\n.sp-progress-buffered,\n.sp-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n}\n\n.sp-progress-buffered {\n background: rgba(255, 255, 255, 0.35);\n}\n\n.sp-progress-played {\n background: var(--sp-accent);\n}\n\n.sp-progress-thumb {\n position: absolute;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--sp-accent);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.1s ease;\n pointer-events: none;\n}\n\n.sp-progress:hover .sp-progress-thumb,\n.sp-progress.sp-dragging .sp-progress-thumb {\n transform: translate(-50%, -50%) scale(1);\n}\n\n.sp-progress-tooltip {\n position: absolute;\n bottom: 18px;\n transform: translateX(-50%);\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 3px;\n font-size: 12px;\n display: none;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-progress:hover .sp-progress-tooltip {\n display: block;\n}\n\n/* ---------- \u63A7\u5236\u6309\u94AE\u884C ---------- */\n.sp-controls {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.sp-controls-spacer {\n flex: 1;\n}\n\n.sp-btn {\n width: var(--sp-control-size);\n height: var(--sp-control-size);\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0;\n opacity: 0.9;\n transition: opacity 0.1s ease, background 0.1s ease;\n}\n\n.sp-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.sp-btn:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.sp-time {\n padding: 0 8px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n opacity: 0.95;\n}\n\n/* ---------- \u5F39\u51FA\u83DC\u5355 ---------- */\n.sp-menu {\n position: relative;\n}\n\n.sp-menu-btn {\n min-width: var(--sp-control-size);\n width: auto;\n padding: 0 8px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.sp-menu-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n min-width: 80px;\n max-height: 220px;\n overflow-y: auto;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-menu.sp-open .sp-menu-panel {\n display: block;\n}\n\n.sp-menu-item {\n display: block;\n width: 100%;\n padding: 7px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: center;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-menu-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-menu-item.sp-active {\n color: var(--sp-accent);\n font-weight: 600;\n}\n\n.sp-menu-empty {\n padding: 7px 16px;\n opacity: 0.5;\n white-space: nowrap;\n}\n\n/* ---------- \u72B6\u6001\u8499\u5C42\uFF1Aloading / \u9519\u8BEF / \u7ED3\u675F ---------- */\n.sp-state {\n position: absolute;\n inset: 0;\n z-index: 15;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.sp-spinner {\n opacity: 0;\n transition: opacity 0.2s ease 0.3s; /* \u5EF6\u8FDF\u51FA\u73B0\uFF0C\u907F\u514D\u77ED\u6682 seek \u95EA\u70C1 */\n}\n\n.sp-state.sp-loading .sp-spinner {\n opacity: 1;\n}\n\n.sp-spinner-ring {\n width: 44px;\n height: 44px;\n border: 4px solid rgba(255, 255, 255, 0.25);\n border-top-color: var(--sp-accent);\n border-radius: 50%;\n animation: sp-spin 0.8s linear infinite;\n}\n\n@keyframes sp-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sp-state-panel {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 14px;\n background: rgba(0, 0, 0, 0.55);\n pointer-events: auto;\n}\n\n.sp-state-message {\n font-size: 15px;\n opacity: 0.95;\n}\n\n.sp-state-actions {\n display: flex;\n gap: 12px;\n}\n\n.sp-state-btn {\n padding: 8px 22px;\n border: 1px solid rgba(255, 255, 255, 0.5);\n border-radius: 18px;\n background: transparent;\n color: var(--sp-text);\n font-size: 14px;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.sp-state-btn:hover {\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-state-btn-primary {\n background: var(--sp-accent);\n border-color: var(--sp-accent);\n}\n\n.sp-state-btn-primary:hover {\n background: var(--sp-accent);\n filter: brightness(1.1);\n}\n\n.sp-state-countdown {\n font-size: 13px;\n opacity: 0.85;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.sp-state-cancel {\n border: none;\n background: transparent;\n color: var(--sp-accent);\n cursor: pointer;\n font-size: 13px;\n padding: 2px 6px;\n}\n\n/* ---------- \u8BBE\u7F6E\u9762\u677F ---------- */\n.sp-settings-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n width: 180px;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-settings.sp-open .sp-settings-panel {\n display: block;\n}\n\n.sp-settings-row {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 7px 12px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n gap: 4px;\n}\n\n.sp-settings-row:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-settings-label {\n flex: 1;\n text-align: left;\n}\n\n.sp-settings-value {\n opacity: 0.6;\n font-size: 12px;\n}\n\n.sp-settings-arrow {\n display: inline-flex;\n align-items: center;\n opacity: 0.5;\n}\n\n.sp-settings-arrow svg {\n width: 16px;\n height: 16px;\n}\n\n.sp-settings-back {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 7px 12px;\n border: none;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n gap: 2px;\n margin-bottom: 2px;\n}\n\n.sp-settings-back:hover {\n background: rgba(255, 255, 255, 0.08);\n}\n\n.sp-settings-back-arrow {\n display: inline-flex;\n align-items: center;\n opacity: 0.7;\n}\n\n.sp-settings-back-arrow svg {\n width: 18px;\n height: 18px;\n}\n\n.sp-settings-sub .sp-menu-item {\n text-align: left;\n padding: 7px 16px;\n}\n\n/* ---------- \u884C\u5185\u5F00\u5173 ---------- */\n.sp-settings-toggle-row {\n cursor: pointer;\n}\n\n.sp-toggle {\n position: relative;\n width: 30px;\n height: 17px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 9px;\n flex-shrink: 0;\n transition: background 0.15s ease;\n outline: none;\n}\n\n.sp-toggle:focus-visible {\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.4);\n}\n\n.sp-toggle.sp-on {\n background: var(--sp-accent);\n}\n\n.sp-toggle-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 13px;\n height: 13px;\n background: #fff;\n border-radius: 50%;\n transition: transform 0.15s ease;\n}\n\n.sp-toggle.sp-on .sp-toggle-thumb {\n transform: translateX(13px);\n}\n\n/* ---------- \u97F3\u91CF ---------- */\n.sp-volume {\n display: flex;\n align-items: center;\n}\n\n.sp-volume-slider {\n width: 0;\n overflow: hidden;\n transition: width 0.15s ease;\n display: flex;\n align-items: center;\n}\n\n.sp-volume:hover .sp-volume-slider,\n.sp-volume.sp-dragging .sp-volume-slider {\n width: 64px;\n}\n\n.sp-volume-track {\n position: relative;\n width: 56px;\n height: var(--sp-bar-height);\n margin: 0 4px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.sp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-text);\n border-radius: 2px;\n}\n\n.sp-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--sp-text);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n";
1253
1413
 
1254
1414
  // src/player.ts
1255
1415
  var DEFAULT_RATES = [0.5, 1, 1.5, 2];
@@ -1688,10 +1848,12 @@ var SweetPlayer = class {
1688
1848
  );
1689
1849
  listen("enterpictureinpicture", () => {
1690
1850
  log("\u64AD\u653E\u5668", "\u8FDB\u5165\u753B\u4E2D\u753B");
1851
+ this.controls.updatePip(true);
1691
1852
  this.emitter.emit("pipchange", true);
1692
1853
  });
1693
1854
  listen("leavepictureinpicture", () => {
1694
1855
  log("\u64AD\u653E\u5668", "\u9000\u51FA\u753B\u4E2D\u753B");
1856
+ this.controls.updatePip(false);
1695
1857
  this.emitter.emit("pipchange", false);
1696
1858
  });
1697
1859
  const onVideoClick = (e) => {
package/dist/index.d.cts CHANGED
@@ -18,7 +18,7 @@ interface LongSeekOptions {
18
18
  stepUpInterval?: number;
19
19
  }
20
20
  /** 可通过 hiddenControls 隐藏的 UI 功能名(只影响显示,不影响 API 与快捷键) */
21
- type ControlName = 'prev' | 'seekBack' | 'play' | 'seekForward' | 'next' | 'time' | 'rate' | 'quality' | 'ratio' | 'audioTrack' | 'volume' | 'pip' | 'fullscreen' | 'title' | 'progress' | 'contextMenu';
21
+ type ControlName = 'prev' | 'seekBack' | 'play' | 'seekForward' | 'next' | 'time' | 'rate' | 'quality' | 'ratio' | 'audioTrack' | 'volume' | 'pip' | 'fullscreen' | 'settings' | 'title' | 'progress' | 'contextMenu';
22
22
  /** 播放器插件:apply 在实例化末尾调用,可返回清理函数(destroy 时执行) */
23
23
  interface SweetPlayerPlugin {
24
24
  name: string;
@@ -221,6 +221,7 @@ interface LocaleStrings {
221
221
  scVolume: string;
222
222
  scFullscreen: string;
223
223
  scMute: string;
224
+ settings: string;
224
225
  copyLog: string;
225
226
  logCopied: string;
226
227
  }
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ interface LongSeekOptions {
18
18
  stepUpInterval?: number;
19
19
  }
20
20
  /** 可通过 hiddenControls 隐藏的 UI 功能名(只影响显示,不影响 API 与快捷键) */
21
- type ControlName = 'prev' | 'seekBack' | 'play' | 'seekForward' | 'next' | 'time' | 'rate' | 'quality' | 'ratio' | 'audioTrack' | 'volume' | 'pip' | 'fullscreen' | 'title' | 'progress' | 'contextMenu';
21
+ type ControlName = 'prev' | 'seekBack' | 'play' | 'seekForward' | 'next' | 'time' | 'rate' | 'quality' | 'ratio' | 'audioTrack' | 'volume' | 'pip' | 'fullscreen' | 'settings' | 'title' | 'progress' | 'contextMenu';
22
22
  /** 播放器插件:apply 在实例化末尾调用,可返回清理函数(destroy 时执行) */
23
23
  interface SweetPlayerPlugin {
24
24
  name: string;
@@ -221,6 +221,7 @@ interface LocaleStrings {
221
221
  scVolume: string;
222
222
  scFullscreen: string;
223
223
  scMute: string;
224
+ settings: string;
224
225
  copyLog: string;
225
226
  logCopied: string;
226
227
  }