dsh-vidi 0.2.2 → 0.3.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.
Files changed (2) hide show
  1. package/lib/client.js +90 -13
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -33858,6 +33858,43 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
33858
33858
  });
33859
33859
  });
33860
33860
 
33861
+ // src/client/extensions.ts
33862
+ function registry() {
33863
+ let items = [];
33864
+ const listeners = /* @__PURE__ */ new Set();
33865
+ const emit = () => {
33866
+ for (const l of listeners) l();
33867
+ };
33868
+ return {
33869
+ get: () => items,
33870
+ subscribe(l) {
33871
+ listeners.add(l);
33872
+ return () => {
33873
+ listeners.delete(l);
33874
+ };
33875
+ },
33876
+ /** Add one; returns the function that removes it. */
33877
+ add(item) {
33878
+ items = [...items.filter((x) => x.id !== item.id), item].sort((a, b) => (a.order ?? 50) - (b.order ?? 50));
33879
+ emit();
33880
+ return () => {
33881
+ items = items.filter((x) => x !== item);
33882
+ emit();
33883
+ };
33884
+ }
33885
+ };
33886
+ }
33887
+ /**
33888
+ * Extension points of the video page, for companion plugins (inject
33889
+ * 'vidiUi'): extra tabs under the player and extra player menus. The page
33890
+ * itself stays in this package; companions only add.
33891
+ */
33892
+ const vidiUi = {
33893
+ version: 1,
33894
+ panels: registry(),
33895
+ playerMenus: registry()
33896
+ };
33897
+
33861
33898
  // src/client/RestreamPanel.tsx
33862
33899
  /** Platforms, in the order people in China most often push to; YouTube and Twitch have one fixed ingest address. */
33863
33900
  const PLATFORMS = [
@@ -35073,24 +35110,45 @@ function LiveView(props) {
35073
35110
  })] });
35074
35111
  /** On the video page the tools share the tab row, leaving the height to the lines. */
35075
35112
  const inline = pageMode && tab === "transcript" && !!job;
35113
+ const extPanels = (0, react.useSyncExternalStore)(vidiUi.panels.subscribe, vidiUi.panels.get);
35114
+ const extMenus = (0, react.useSyncExternalStore)(vidiUi.playerMenus.subscribe, vidiUi.playerMenus.get);
35115
+ const view = job ? {
35116
+ job,
35117
+ cues,
35118
+ sessionId,
35119
+ now,
35120
+ seek: (s) => player.current?.seek(s),
35121
+ style: subStyle,
35122
+ lines: shownLines,
35123
+ ui
35124
+ } : void 0;
35076
35125
  /** Subtitle lines and glossary: under the player on the video page, beside it in a conversation. */
35077
35126
  const transcriptBox = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
35078
35127
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
35079
35128
  className: "dshl-sidetabs",
35080
35129
  role: "tablist",
35081
- children: [["transcript", "glossary"].map((k) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
35082
- role: "tab",
35083
- className: tab === k ? "on" : "",
35084
- onClick: () => setTab(k),
35085
- children: t(`tab.${k}`)
35086
- }, k)), inline && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
35087
- className: "dshl-tools inline",
35088
- children: [
35089
- trMode === "lines" && trSearch,
35090
- trSeg,
35091
- trAction
35092
- ]
35093
- })]
35130
+ children: [
35131
+ ["transcript", "glossary"].map((k) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
35132
+ role: "tab",
35133
+ className: tab === k ? "on" : "",
35134
+ onClick: () => setTab(k),
35135
+ children: t(`tab.${k}`)
35136
+ }, k)),
35137
+ job && extPanels.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
35138
+ role: "tab",
35139
+ className: tab === p.id ? "on" : "",
35140
+ onClick: () => setTab(p.id),
35141
+ children: p.label(ui)
35142
+ }, p.id)),
35143
+ inline && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
35144
+ className: "dshl-tools inline",
35145
+ children: [
35146
+ trMode === "lines" && trSearch,
35147
+ trSeg,
35148
+ trAction
35149
+ ]
35150
+ })
35151
+ ]
35094
35152
  }),
35095
35153
  !job && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
35096
35154
  className: "dshl-panel dshl-muted",
@@ -35172,6 +35230,10 @@ function LiveView(props) {
35172
35230
  })
35173
35231
  ]
35174
35232
  }),
35233
+ job && view && extPanels.filter((p) => p.id === tab).map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
35234
+ className: "dshl-panel dshl-ext",
35235
+ children: p.render(view)
35236
+ }, p.id)),
35175
35237
  tab === "glossary" && job && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GlossaryPanel, {
35176
35238
  jobId: job.id,
35177
35239
  terms,
@@ -35408,6 +35470,12 @@ function LiveView(props) {
35408
35470
  t
35409
35471
  })
35410
35472
  },
35473
+ ...extMenus.map((m) => ({
35474
+ id: `ext:${m.id}`,
35475
+ icon: m.icon,
35476
+ title: m.title(ui),
35477
+ content: view ? m.render(view, () => {}) : null
35478
+ })),
35411
35479
  {
35412
35480
  id: "download",
35413
35481
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.download, {}),
@@ -36761,6 +36829,14 @@ const CSS = `
36761
36829
  .dshl-toast { position: fixed; left: 50%; bottom: 28px; z-index: 2147483001; max-width: min(560px, calc(100vw - 32px)); padding: 10px 16px; border-radius: 10px;
36762
36830
  background: rgba(28,28,31,.95); color: #f2f2f3; font: 13.5px/1.5 system-ui, -apple-system, sans-serif; box-shadow: 0 10px 30px rgba(0,0,0,.3); transform: translateX(-50%); pointer-events: none; }
36763
36831
  .dshl-pagewrap { position: absolute; inset: 0; z-index: 1; pointer-events: auto; background: var(--dsw-alias-bg-base, #fff); }
36832
+ /* Desktop app: the page covers the whole window. Windows: start under the title bar. macOS: the
36833
+ traffic lights sit over the top-left, so the top bar leaves them room, lines its controls up
36834
+ with them, and its empty space moves the window like the app's own top strip. Full screen and
36835
+ the web page need none of this. */
36836
+ .dshl-pagewrap { top: var(--dsh-frame-chrome-top, 0px); }
36837
+ html[data-platform='darwin']:not([data-fullscreen]) .dshl.page .dshl-top { padding: 4px 16px 8px 84px; -webkit-app-region: drag; app-region: drag; }
36838
+ html[data-platform='darwin']:not([data-fullscreen]) .dshl.page .dshl-top :is(button, input, label, a, select, .dshl-pick) { -webkit-app-region: no-drag; app-region: no-drag; }
36839
+ @media (max-width: 600px) { html[data-platform='darwin']:not([data-fullscreen]) .dshl.page .dshl-top { padding-left: 80px; } }
36764
36840
  .dshl.page { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
36765
36841
  .dshl.page .dshl-main { flex: 1; min-height: 0; grid-template-columns: minmax(0, 1fr) clamp(320px, 30vw, 420px); align-items: stretch; padding-bottom: 16px; }
36766
36842
  /* Video page: player on top, then the status row, then subtitles/glossary filling what is left; chat on the right. */
@@ -37424,6 +37500,7 @@ function apply(ctx) {
37424
37500
  zh,
37425
37501
  en
37426
37502
  }), "dsh-vidi: dictionaries");
37503
+ ctx.effect(() => ctx.provide("vidiUi", vidiUi), "dsh-vidi: page extensions");
37427
37504
  const t = ctx.locale.bind(NS);
37428
37505
  const extra = {};
37429
37506
  ctx.inject(["uiWorkspace"], (c) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vidi",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "A video tool for DeepSeek Harness: watch live streams, videos and podcasts with real-time subtitles in any language, then clip and download",
5
5
  "type": "module",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",