dsh-plugin-subscriptions 0.3.1 → 0.4.1

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
@@ -71,7 +71,10 @@ const en = {
71
71
  imageLoading: "Loading…",
72
72
  imageLoadFailed: "Retry",
73
73
  imagePreview: "Image preview",
74
- imageClose: "Close"
74
+ imageClose: "Close",
75
+ generatingVideo: "Generating video…",
76
+ videoLoading: "Loading video…",
77
+ videoLoadFailed: "Video failed to load: {message}"
75
78
  };
76
79
  /** zh strings, one per {@link en} key. */
77
80
  const zh = {
@@ -110,13 +113,16 @@ const zh = {
110
113
  imageLoading: "加载中…",
111
114
  imageLoadFailed: "重试",
112
115
  imagePreview: "图片预览",
113
- imageClose: "关闭"
116
+ imageClose: "关闭",
117
+ generatingVideo: "正在生成视频…",
118
+ videoLoading: "视频加载中…",
119
+ videoLoadFailed: "视频加载失败:{message}"
114
120
  };
115
121
 
116
122
  //#endregion
117
123
  //#region src/client/SubscriptionsSection.tsx
118
124
  /** Logical RPC channel served by the node half of this plugin. */
119
- const SUBSCRIPTIONS_AUTH_CHANNEL$1 = "/subscriptions-auth";
125
+ const SUBSCRIPTIONS_AUTH_CHANNEL$2 = "/subscriptions-auth";
120
126
  /** Poll cadence while a provider login attempt is busy. */
121
127
  const POLL_INTERVAL_MS = 2e3;
122
128
  /** Card display metadata, in page order (names are brand names, not translated). */
@@ -146,7 +152,7 @@ var SubscriptionsAuthError = class extends Error {};
146
152
  async function callSubscriptionsAuth$1(rpc, endpoint, payload) {
147
153
  let result;
148
154
  try {
149
- result = await rpc.call(SUBSCRIPTIONS_AUTH_CHANNEL$1, endpoint, payload);
155
+ result = await rpc.call(SUBSCRIPTIONS_AUTH_CHANNEL$2, endpoint, payload);
150
156
  } catch (error) {
151
157
  throw new SubscriptionsAuthError(error instanceof Error ? error.message : String(error));
152
158
  }
@@ -164,12 +170,12 @@ function messageOf(error) {
164
170
  * @param params - `{name}` template params.
165
171
  * @returns the template with params substituted.
166
172
  */
167
- function fallbackTranslate$1(key, params) {
173
+ function fallbackTranslate$2(key, params) {
168
174
  let text = en[key];
169
175
  for (const [name, value] of Object.entries(params ?? {})) text = text.replaceAll(`{${name}}`, String(value));
170
176
  return text;
171
177
  }
172
- const styles$1 = {
178
+ const styles$2 = {
173
179
  section: {
174
180
  display: "flex",
175
181
  flexDirection: "column",
@@ -382,7 +388,7 @@ function usageBarColor(usedPercent) {
382
388
  */
383
389
  function SubscriptionsSection(props) {
384
390
  const { rpc } = props;
385
- const t = props.t ?? fallbackTranslate$1;
391
+ const t = props.t ?? fallbackTranslate$2;
386
392
  const [statuses, setStatuses] = (0, react.useState)({});
387
393
  const [errors, setErrors] = (0, react.useState)({});
388
394
  const [manualDrafts, setManualDrafts] = (0, react.useState)({
@@ -589,13 +595,13 @@ function SubscriptionsSection(props) {
589
595
  refresh
590
596
  ]);
591
597
  if (rpc === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
592
- style: styles$1.intro,
598
+ style: styles$2.intro,
593
599
  children: t("unavailable")
594
600
  });
595
601
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
596
- style: styles$1.section,
602
+ style: styles$2.section,
597
603
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
598
- style: styles$1.intro,
604
+ style: styles$2.intro,
599
605
  children: t("intro")
600
606
  }), PROVIDERS.map(({ id, name }) => {
601
607
  const status = statuses[id];
@@ -604,36 +610,36 @@ function SubscriptionsSection(props) {
604
610
  const usageError = usageErrors[id];
605
611
  const showUsage = status?.loggedIn === true && usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[id] === true);
606
612
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
607
- style: styles$1.card,
613
+ style: styles$2.card,
608
614
  children: [
609
615
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
610
- style: styles$1.cardHeader,
616
+ style: styles$2.cardHeader,
611
617
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
612
- ...styles$1.dot,
618
+ ...styles$2.dot,
613
619
  background: dotColor(status)
614
620
  } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
615
- style: styles$1.name,
621
+ style: styles$2.name,
616
622
  children: name
617
623
  })]
618
624
  }),
619
625
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
620
- style: styles$1.statusLine,
626
+ style: styles$2.statusLine,
621
627
  children: statusText(t, status)
622
628
  }),
623
629
  status?.detail !== void 0 && status.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
624
- style: styles$1.statusLine,
630
+ style: styles$2.statusLine,
625
631
  children: status.detail
626
632
  }),
627
633
  errors[id] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
628
- style: styles$1.errorLine,
634
+ style: styles$2.errorLine,
629
635
  children: errors[id]
630
636
  }),
631
637
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
632
- style: styles$1.actions,
638
+ style: styles$2.actions,
633
639
  children: [
634
640
  !busy && status?.loggedIn !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
635
641
  type: "button",
636
- style: styles$1.button,
642
+ style: styles$2.button,
637
643
  onClick: () => {
638
644
  login(id);
639
645
  },
@@ -641,7 +647,7 @@ function SubscriptionsSection(props) {
641
647
  }),
642
648
  busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
643
649
  type: "button",
644
- style: styles$1.button,
650
+ style: styles$2.button,
645
651
  onClick: () => {
646
652
  cancel(id);
647
653
  },
@@ -649,7 +655,7 @@ function SubscriptionsSection(props) {
649
655
  }),
650
656
  status?.loggedIn === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
651
657
  type: "button",
652
- style: styles$1.button,
658
+ style: styles$2.button,
653
659
  onClick: () => {
654
660
  logout(id, name);
655
661
  },
@@ -658,23 +664,23 @@ function SubscriptionsSection(props) {
658
664
  ]
659
665
  }),
660
666
  showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
661
- style: styles$1.usage,
667
+ style: styles$2.usage,
662
668
  children: [
663
669
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
664
- style: styles$1.usageHeader,
670
+ style: styles$2.usageHeader,
665
671
  children: [
666
672
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
667
- style: styles$1.usageTitle,
673
+ style: styles$2.usageTitle,
668
674
  children: t("usageTitle")
669
675
  }),
670
676
  usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
671
- style: styles$1.usagePlan,
677
+ style: styles$2.usagePlan,
672
678
  children: t("usagePlan", { plan: usage.plan })
673
679
  }),
674
680
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
675
681
  type: "button",
676
682
  style: {
677
- ...styles$1.usageRefresh,
683
+ ...styles$2.usageRefresh,
678
684
  ...usageLoading[id] === true ? {
679
685
  opacity: .5,
680
686
  cursor: "default"
@@ -689,28 +695,28 @@ function SubscriptionsSection(props) {
689
695
  ]
690
696
  }),
691
697
  usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
692
- style: styles$1.statusLine,
698
+ style: styles$2.statusLine,
693
699
  children: t("usageLoading")
694
700
  }),
695
701
  usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
696
- style: styles$1.errorLine,
702
+ style: styles$2.errorLine,
697
703
  children: t("usageError", { message: usageError })
698
704
  }),
699
705
  usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
700
- style: styles$1.statusLine,
706
+ style: styles$2.statusLine,
701
707
  children: t("usageEmpty")
702
708
  }),
703
709
  (usage?.windows ?? []).map((window$1, index) => {
704
710
  const percent = Math.min(100, Math.max(0, window$1.usedPercent));
705
711
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
706
- style: styles$1.usageRow,
712
+ style: styles$2.usageRow,
707
713
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
708
- style: styles$1.usageMeta,
714
+ style: styles$2.usageMeta,
709
715
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: usageWindowLabel(t, window$1) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [`${String(Math.round(percent))}%`, window$1.resetsAt !== void 0 && ` · ${t("usageResets", { date: new Date(window$1.resetsAt).toLocaleString() })}`] })]
710
716
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
711
- style: styles$1.usageTrack,
717
+ style: styles$2.usageTrack,
712
718
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
713
- ...styles$1.usageFill,
719
+ ...styles$2.usageFill,
714
720
  width: `${String(percent)}%`,
715
721
  background: usageBarColor(percent)
716
722
  } })
@@ -720,11 +726,11 @@ function SubscriptionsSection(props) {
720
726
  ]
721
727
  }),
722
728
  busy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
723
- style: styles$1.manual,
729
+ style: styles$2.manual,
724
730
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("manualSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
725
- style: styles$1.manualRow,
731
+ style: styles$2.manualRow,
726
732
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
727
- style: styles$1.manualInput,
733
+ style: styles$2.manualInput,
728
734
  value: manualDrafts[id],
729
735
  placeholder: t("manualPlaceholder"),
730
736
  onChange: (event) => setManualDrafts((prev) => ({
@@ -733,7 +739,7 @@ function SubscriptionsSection(props) {
733
739
  }))
734
740
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
735
741
  type: "button",
736
- style: styles$1.button,
742
+ style: styles$2.button,
737
743
  onClick: () => {
738
744
  submitManual(id);
739
745
  },
@@ -750,9 +756,9 @@ function SubscriptionsSection(props) {
750
756
  //#endregion
751
757
  //#region src/client/ImageGenerateToolview.tsx
752
758
  /** Logical RPC channel served by the node half of this plugin. */
753
- const SUBSCRIPTIONS_AUTH_CHANNEL = "/subscriptions-auth";
759
+ const SUBSCRIPTIONS_AUTH_CHANNEL$1 = "/subscriptions-auth";
754
760
  /** Title prompt truncation budget (characters). */
755
- const PROMPT_MAX_LENGTH = 60;
761
+ const PROMPT_MAX_LENGTH$1 = 60;
756
762
  /**
757
763
  * Call one `/subscriptions-auth` endpoint and unwrap the business result.
758
764
  * @param rpc - Connection RPC caller.
@@ -761,7 +767,7 @@ const PROMPT_MAX_LENGTH = 60;
761
767
  * @returns the success value, cast by the caller to the endpoint's shape.
762
768
  */
763
769
  async function callSubscriptionsAuth(rpc, endpoint, payload) {
764
- const result = await rpc.call(SUBSCRIPTIONS_AUTH_CHANNEL, endpoint, payload);
770
+ const result = await rpc.call(SUBSCRIPTIONS_AUTH_CHANNEL$1, endpoint, payload);
765
771
  if (!result.ok) throw new Error(result.error.message);
766
772
  return result.value;
767
773
  }
@@ -780,13 +786,13 @@ function createImageLoader(rpc) {
780
786
  * @param params - `{name}` template params.
781
787
  * @returns the template with params substituted.
782
788
  */
783
- function fallbackTranslate(key, params) {
789
+ function fallbackTranslate$1(key, params) {
784
790
  let text = en[key];
785
791
  for (const [name, value] of Object.entries(params ?? {})) text = text.replaceAll(`{${name}}`, String(value));
786
792
  return text;
787
793
  }
788
794
  /** Extract the prompt from the call's raw args JSON; falls back to the first string value, then the raw line. */
789
- function derivePrompt(argsRaw) {
795
+ function derivePrompt$1(argsRaw) {
790
796
  let parsed;
791
797
  try {
792
798
  parsed = JSON.parse(argsRaw);
@@ -803,10 +809,10 @@ function derivePrompt(argsRaw) {
803
809
  }
804
810
  }
805
811
  const line = (prompt ?? argsRaw).split("\n", 1)[0] ?? "";
806
- return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
812
+ return line.length > PROMPT_MAX_LENGTH$1 ? `${line.slice(0, PROMPT_MAX_LENGTH$1)}…` : line;
807
813
  }
808
814
  /** Flatten a settled result's text blocks (the degraded text-only route and the error line). */
809
- function resultText(block) {
815
+ function resultText$1(block) {
810
816
  if (!("kind" in block)) return "";
811
817
  const parts = [];
812
818
  for (const part of block.content) if (part.type === "text") parts.push(part.text);
@@ -820,7 +826,7 @@ function resultImages(block) {
820
826
  for (const part of block.content) if (part.type === "image") images.push({ attachment: part.attachment });
821
827
  return images;
822
828
  }
823
- const styles = {
829
+ const styles$1 = {
824
830
  container: {
825
831
  display: "flex",
826
832
  flexDirection: "column",
@@ -874,12 +880,12 @@ const styles = {
874
880
  */
875
881
  function ImageGenerateToolview(props) {
876
882
  const { block, load } = props;
877
- const t = props.t ?? fallbackTranslate;
883
+ const t = props.t ?? fallbackTranslate$1;
878
884
  if (block === void 0) return null;
879
885
  const settled = "kind" in block;
880
- const title = `image_generate: ${derivePrompt((settled ? block.call?.argsRaw : block.argsRaw) ?? "")}`;
886
+ const title = `image_generate: ${derivePrompt$1((settled ? block.call?.argsRaw : block.argsRaw) ?? "")}`;
881
887
  const images = resultImages(block);
882
- const text = settled ? resultText(block) : "";
888
+ const text = settled ? resultText$1(block) : "";
883
889
  const labels = {
884
890
  image: t("image"),
885
891
  open: t("viewImage"),
@@ -892,24 +898,24 @@ function ImageGenerateToolview(props) {
892
898
  }
893
899
  };
894
900
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
895
- style: styles.container,
901
+ style: styles$1.container,
896
902
  children: [
897
903
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
898
- style: styles.row,
904
+ style: styles$1.row,
899
905
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
900
- style: styles.icon,
906
+ style: styles$1.icon,
901
907
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 })
902
908
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
903
- style: styles.title,
909
+ style: styles$1.title,
904
910
  children: title
905
911
  })]
906
912
  }),
907
913
  !settled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
908
- style: styles.subtle,
914
+ style: styles$1.subtle,
909
915
  children: t("generating")
910
916
  }),
911
917
  settled && block.isError && text !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
912
- style: styles.error,
918
+ style: styles$1.error,
913
919
  children: text.split("\n", 1)[0]
914
920
  }),
915
921
  settled && !block.isError && images.length > 0 && load !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_attachment.ImageGallery, {
@@ -919,6 +925,221 @@ function ImageGenerateToolview(props) {
919
925
  labels
920
926
  }),
921
927
  settled && !block.isError && images.length === 0 && text !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
928
+ style: styles$1.output,
929
+ children: text
930
+ })
931
+ ]
932
+ });
933
+ }
934
+
935
+ //#endregion
936
+ //#region src/client/VideoGenerateToolview.tsx
937
+ /** Logical RPC channel served by the node half of this plugin. */
938
+ const SUBSCRIPTIONS_AUTH_CHANNEL = "/subscriptions-auth";
939
+ /** Title prompt truncation budget (characters). */
940
+ const PROMPT_MAX_LENGTH = 60;
941
+ /**
942
+ * Build the video loader over the `/subscriptions-auth` `video` endpoint.
943
+ * @param rpc - Connection RPC caller.
944
+ * @returns loader resolving a bare file name to the decoded bytes.
945
+ */
946
+ function createVideoLoader(rpc) {
947
+ return async (name) => {
948
+ const result = await rpc.call(SUBSCRIPTIONS_AUTH_CHANNEL, "video", { name });
949
+ if (!result.ok) throw new Error(result.error.message);
950
+ return result.value;
951
+ };
952
+ }
953
+ /**
954
+ * English-dictionary fallback for a missing locale seat (standalone renders);
955
+ * the framework always supplies the namespace-bound one.
956
+ */
957
+ function fallbackTranslate(key, params) {
958
+ let text = en[key];
959
+ for (const [name, value] of Object.entries(params ?? {})) text = text.replaceAll(`{${name}}`, String(value));
960
+ return text;
961
+ }
962
+ /** Extract the prompt from the call's raw args JSON; falls back to the first string value, then the raw line. */
963
+ function derivePrompt(argsRaw) {
964
+ let parsed;
965
+ try {
966
+ parsed = JSON.parse(argsRaw);
967
+ } catch {
968
+ parsed = void 0;
969
+ }
970
+ let prompt;
971
+ if (typeof parsed === "object" && parsed !== null) {
972
+ const args = parsed;
973
+ if (typeof args.prompt === "string" && args.prompt !== "") prompt = args.prompt;
974
+ else for (const value of Object.values(args)) if (typeof value === "string" && value !== "") {
975
+ prompt = value;
976
+ break;
977
+ }
978
+ }
979
+ const line = (prompt ?? argsRaw).split("\n", 1)[0] ?? "";
980
+ return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
981
+ }
982
+ /** Flatten a settled result's text blocks (the fallback body and the error line). */
983
+ function resultText(block) {
984
+ if (!("kind" in block)) return "";
985
+ const parts = [];
986
+ for (const part of block.content) if (part.type === "text") parts.push(part.text);
987
+ if (parts.length === 0 && block.error !== void 0) parts.push(`${block.error.name}: ${block.error.code}`);
988
+ return parts.join("\n");
989
+ }
990
+ /**
991
+ * The generated video's bare file name: presentation meta first (top-level
992
+ * dispatches), then the render text's "Saved video to …" line (nested
993
+ * dispatches compute no meta).
994
+ */
995
+ function resolveFileName(block) {
996
+ if (!("kind" in block)) return void 0;
997
+ const meta = block.meta;
998
+ if (typeof meta === "object" && meta !== null) {
999
+ const fileName = meta.fileName;
1000
+ if (typeof fileName === "string" && fileName.length > 0) return fileName;
1001
+ }
1002
+ const match = /^Saved video to (.+\.mp4)/m.exec(resultText(block));
1003
+ if (match === null) return void 0;
1004
+ const path = match[1];
1005
+ return path.slice(path.lastIndexOf("/") + 1);
1006
+ }
1007
+ /** Decode a base64 payload into bytes (browser-side; no Buffer). */
1008
+ function base64Bytes(dataBase64) {
1009
+ const binary = atob(dataBase64);
1010
+ const bytes = new Uint8Array(binary.length);
1011
+ for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
1012
+ return bytes;
1013
+ }
1014
+ const styles = {
1015
+ container: {
1016
+ display: "flex",
1017
+ flexDirection: "column",
1018
+ gap: 6,
1019
+ padding: "4px 0"
1020
+ },
1021
+ row: {
1022
+ display: "flex",
1023
+ alignItems: "center",
1024
+ gap: 6,
1025
+ minWidth: 0
1026
+ },
1027
+ icon: {
1028
+ display: "inline-flex",
1029
+ flexShrink: 0,
1030
+ color: "var(--dsw-alias-label-tertiary)"
1031
+ },
1032
+ title: {
1033
+ fontSize: 13,
1034
+ lineHeight: "20px",
1035
+ color: "var(--dsw-alias-label-primary)",
1036
+ overflow: "hidden",
1037
+ textOverflow: "ellipsis",
1038
+ whiteSpace: "nowrap"
1039
+ },
1040
+ subtle: {
1041
+ margin: 0,
1042
+ fontSize: 12,
1043
+ lineHeight: "18px",
1044
+ color: "var(--dsw-alias-label-tertiary)"
1045
+ },
1046
+ output: {
1047
+ margin: 0,
1048
+ fontSize: 12,
1049
+ lineHeight: "18px",
1050
+ color: "var(--dsw-alias-label-secondary)",
1051
+ whiteSpace: "pre-wrap",
1052
+ overflowWrap: "anywhere"
1053
+ },
1054
+ error: {
1055
+ margin: 0,
1056
+ fontSize: 12,
1057
+ lineHeight: "18px",
1058
+ color: "var(--dsw-alias-state-error-primary)"
1059
+ },
1060
+ video: {
1061
+ display: "block",
1062
+ maxWidth: 480,
1063
+ width: "100%",
1064
+ borderRadius: 8,
1065
+ backgroundColor: "var(--dsw-alias-fill-tertiary)"
1066
+ }
1067
+ };
1068
+ /**
1069
+ * The `video_generate` keyed toolview component.
1070
+ * @param props - owner share, inject face, and locale seat (spread flat).
1071
+ * @returns the call row plus, once settled, the player / text / error body.
1072
+ */
1073
+ function VideoGenerateToolview(props) {
1074
+ const { block, loadVideo } = props;
1075
+ const t = props.t ?? fallbackTranslate;
1076
+ const settled = block !== void 0 && "kind" in block;
1077
+ const isError = settled && block.isError;
1078
+ const fileName = block !== void 0 && settled && !isError ? resolveFileName(block) : void 0;
1079
+ const [load, setLoad] = (0, react.useState)({ phase: "loading" });
1080
+ (0, react.useEffect)(() => {
1081
+ if (fileName === void 0 || loadVideo === void 0) return;
1082
+ let cancelled = false;
1083
+ let objectUrl;
1084
+ setLoad({ phase: "loading" });
1085
+ loadVideo(fileName).then((video) => {
1086
+ if (cancelled) return;
1087
+ objectUrl = URL.createObjectURL(new Blob([base64Bytes(video.dataBase64).slice()], { type: video.mediaType }));
1088
+ setLoad({
1089
+ phase: "ready",
1090
+ url: objectUrl
1091
+ });
1092
+ }, (error) => {
1093
+ if (cancelled) return;
1094
+ setLoad({
1095
+ phase: "failed",
1096
+ message: error instanceof Error ? error.message : String(error)
1097
+ });
1098
+ });
1099
+ return () => {
1100
+ cancelled = true;
1101
+ if (objectUrl !== void 0) URL.revokeObjectURL(objectUrl);
1102
+ };
1103
+ }, [fileName, loadVideo]);
1104
+ if (block === void 0) return null;
1105
+ const title = `video_generate: ${derivePrompt((settled ? block.call?.argsRaw : block.argsRaw) ?? "")}`;
1106
+ const text = settled ? resultText(block) : "";
1107
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1108
+ style: styles.container,
1109
+ children: [
1110
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1111
+ style: styles.row,
1112
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1113
+ style: styles.icon,
1114
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 })
1115
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1116
+ style: styles.title,
1117
+ children: title
1118
+ })]
1119
+ }),
1120
+ !settled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1121
+ style: styles.subtle,
1122
+ children: t("generatingVideo")
1123
+ }),
1124
+ settled && isError && text !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1125
+ style: styles.error,
1126
+ children: text.split("\n", 1)[0]
1127
+ }),
1128
+ settled && !isError && fileName !== void 0 && load.phase === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1129
+ style: styles.subtle,
1130
+ children: t("videoLoading")
1131
+ }),
1132
+ settled && !isError && fileName !== void 0 && load.phase === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1133
+ style: styles.error,
1134
+ children: t("videoLoadFailed", { message: load.message })
1135
+ }),
1136
+ settled && !isError && fileName !== void 0 && load.phase === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
1137
+ style: styles.video,
1138
+ src: load.url,
1139
+ controls: true,
1140
+ preload: "metadata"
1141
+ }),
1142
+ settled && !isError && fileName === void 0 && text !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
922
1143
  style: styles.output,
923
1144
  children: text
924
1145
  })
@@ -971,6 +1192,13 @@ function apply(ctx) {
971
1192
  locale: NS,
972
1193
  inject: toolviewInjected
973
1194
  }, ImageGenerateToolview));
1195
+ const videoToolviewInjected = () => ({ loadVideo: createVideoLoader(connection.rpc) });
1196
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1197
+ name: "tool.call.toolview",
1198
+ key: "video_generate",
1199
+ locale: NS,
1200
+ inject: videoToolviewInjected
1201
+ }, VideoGenerateToolview));
974
1202
  }
975
1203
 
976
1204
  //#endregion