dsh-connect-minimaxcode 0.6.1 → 0.6.2

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.en.md CHANGED
@@ -384,6 +384,13 @@ The OAuth token is valid for about an hour and is refreshed automatically by the
384
384
 
385
385
  ## Changelog
386
386
 
387
+ ### 0.6.2 (2026-11-11)
388
+
389
+ - **Fixed "could not send it to the conversation"**: the previous version looked for the session among the slot's props, but the host hands the session to the slot's **injection callback** (the slot is session-scoped), so the identifier was always missing and the picture had nowhere to go. The session is now taken the way the host gives it.
390
+ - **One more way to deliver**: when the host offers no session face to send with, the plugin falls back to the input box's own attachment channel — the very road a picture dragged in by hand takes, so it appears in the input box and you press send.
391
+ - The picture's arrival is the feedback, so nothing is left on screen and the composer stays clean.
392
+ - `tests/generate-dock.spec.ts` rewritten to drive the plugin **the way the host does** (6 cases): the session identifier arrives through the injection, the picture travels as `{type:"image", mediaType, data}` with the prompt, the fallback into the input box when there is no way to send, and the message shown when neither is there.
393
+
387
394
  ### 0.6.1 (2026-11-11)
388
395
 
389
396
  - **A generated picture can now go straight into the conversation**: the composer tool row gained a **Generate an image** entry. Write a prompt, press generate, and the picture is sent as part of this turn — through the host's own road (`ctx.sessions.binding(id).session.prompt(content, mode, signal, requestId)`, the very call the send button makes), so the host validates it, commits it as a durable attachment and treats it as an ordinary message.
package/README.md CHANGED
@@ -395,6 +395,13 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
395
395
 
396
396
  ## 更新记录
397
397
 
398
+ ### 0.6.2(2026-11-11)
399
+
400
+ - **修复「无法发送到对话」**:上一版从插槽的 props 里找会话标识,但宿主是把会话交给**插槽的注入回调**的(该插槽是会话作用域的),于是标识始终为空,图片自然送不出去。现在按宿主的方式取会话标识。
401
+ - **多一条投递路径**:宿主若不提供直接发送的会话面,插件会退回到输入框自己的附件通道 —— 与手动拖一张图进输入框完全同路,图片出现在输入框里,由你按下发送。
402
+ - 图片到达本身就是反馈,成功后不再留提示条,输入框保持干净。
403
+ - `tests/generate-dock.spec.ts` 重写为**按宿主的真实投递方式**驱动插件(6 个用例):会话标识来自注入回调、图片作为 `{type:"image", mediaType, data}` 随提示词发出、没有发送通道时退回输入框、以及两条路都没有时的提示。
404
+
398
405
  ### 0.6.1(2026-11-11)
399
406
 
400
407
  - **生成的图片现在能直接进对话**:输入框工具行新增一个「生成图片」入口,写下提示词点生成,图片会作为**这一轮消息的一部分**发给模型 —— 走的是宿主自己那条路(`ctx.sessions.binding(id).session.prompt(content, mode, signal, requestId)`,与输入框按发送键完全相同),因此图片会被宿主校验、落成持久附件,并按普通消息参与对话。
package/lib/client.js CHANGED
@@ -97,6 +97,7 @@
97
97
  generateToChat: '生成图片',
98
98
  generateToChatHint: '生成的图片会作为这一轮消息的一部分发给模型。',
99
99
  generateSent: '图片已发送到对话',
100
+ generateDrafted: '图片已放入输入框',
100
101
  generateSendFailed: '无法发送到对话,请到插件页生成后下载。',
101
102
  };
102
103
  const en = {
@@ -168,6 +169,7 @@
168
169
  generateToChat: 'Generate an image',
169
170
  generateToChatHint: 'The generated picture is sent as part of this turn.',
170
171
  generateSent: 'The picture is on its way to the conversation',
172
+ generateDrafted: 'The picture is in the input box',
171
173
  generateSendFailed: 'Could not send it to the conversation; generate on the plugin page and download it instead.',
172
174
  };
173
175
  const loader = globalThis.__ModuleLoader__;
@@ -455,6 +457,14 @@
455
457
  const [prompt, setPrompt] = React.useState('');
456
458
  const [busy, setBusy] = React.useState(false);
457
459
  const [note, setNote] = React.useState('');
460
+ /**
461
+ * The session the picture belongs to.
462
+ *
463
+ * The host hands the session id to the slot through its injection,
464
+ * because the slot is session-scoped; it is also carried on the
465
+ * standard props, so either may deliver it.
466
+ */
467
+ const sessionId = () => props.sessionId;
458
468
  /**
459
469
  * The session's own send path.
460
470
  *
@@ -463,13 +473,43 @@
463
473
  * sent this way is a normal turn.
464
474
  */
465
475
  const sessionFace = () => {
466
- const sessionId = props.sessionId;
467
- if (sessionId === undefined)
476
+ const id = sessionId();
477
+ if (id === undefined)
468
478
  return undefined;
469
- const binding = clientContext?.sessions?.binding?.(sessionId);
479
+ const binding = clientContext?.sessions?.binding?.(id);
470
480
  const send = binding?.session?.prompt;
471
481
  return typeof send === 'function' ? { prompt: send } : undefined;
472
482
  };
483
+ /**
484
+ * The conversation's own draft channel.
485
+ *
486
+ * The composer builds a draft from a browser File and hands its id to
487
+ * the input box, which is how a picture attached by hand appears
488
+ * there. Using the same two steps leaves the picture in the input box
489
+ * for the person to send, rather than sending on their behalf.
490
+ */
491
+ const draftFace = () => {
492
+ const id = sessionId();
493
+ if (id === undefined)
494
+ return undefined;
495
+ const scoped = clientContext?.sessions?.scope?.(id);
496
+ const conversation = scoped?.get?.('conversation');
497
+ const create = conversation?.createDrafts;
498
+ return typeof create === 'function'
499
+ ? { createDrafts: create.bind(conversation) }
500
+ : undefined;
501
+ };
502
+ /** The bytes behind a data URL. */
503
+ const base64Bytes = (dataUrl) => {
504
+ const comma = dataUrl.indexOf(',');
505
+ const base64 = comma < 0 ? dataUrl : dataUrl.slice(comma + 1);
506
+ const binary = atob(base64);
507
+ const bytes = new Uint8Array(new ArrayBuffer(binary.length));
508
+ for (let index = 0; index < binary.length; index += 1) {
509
+ bytes[index] = binary.charCodeAt(index);
510
+ }
511
+ return bytes;
512
+ };
473
513
  /** Turn a data URL into the parts the session's admission accepts. */
474
514
  const imagePart = (dataUrl, mediaType) => {
475
515
  const comma = dataUrl.indexOf(',');
@@ -495,15 +535,34 @@
495
535
  return;
496
536
  }
497
537
  const part = imagePart(body.dataUrl, body.mediaType ?? 'image/jpeg');
538
+ if (part === undefined) {
539
+ setNote(t('generateFailed'));
540
+ return;
541
+ }
542
+ // Sending the picture as a turn is what the dock promises; the
543
+ // draft channel is the fallback for a host that only offers that.
498
544
  const face = sessionFace();
499
- if (part === undefined || face === undefined) {
500
- setNote(t('generateSendFailed'));
545
+ if (face !== undefined) {
546
+ await face.prompt([part, { type: 'text', text }], 'queue', undefined, `minimax-h3-${Date.now()}`);
547
+ setPrompt('');
548
+ setOpen(false);
549
+ // The picture's own arrival is the feedback: it is in the
550
+ // conversation now, so the composer needs no banner.
551
+ setNote('');
501
552
  return;
502
553
  }
503
- await face.prompt([part, { type: 'text', text }], 'queue', undefined, `minimax-h3-${Date.now()}`);
504
- setPrompt('');
505
- setOpen(false);
506
- setNote(t('generateSent'));
554
+ const drafts = draftFace();
555
+ const addAttachments = props.inputActions?.addAttachments;
556
+ if (drafts !== undefined && typeof addAttachments === 'function') {
557
+ const file = new File([base64Bytes(body.dataUrl)], 'minimax-h3-image.jpg', { type: body.mediaType ?? 'image/jpeg' });
558
+ const created = drafts.createDrafts(sessionId(), [file], true);
559
+ addAttachments(created.map(draft => draft.id));
560
+ setPrompt('');
561
+ setOpen(false);
562
+ setNote('');
563
+ return;
564
+ }
565
+ setNote(t('generateSendFailed'));
507
566
  }
508
567
  catch {
509
568
  setNote(t('generateSendFailed'));
@@ -997,7 +1056,10 @@
997
1056
  id: 'minimax-generate',
998
1057
  order: 5,
999
1058
  locale: NAMESPACE,
1000
- inject: () => ({ t }),
1059
+ // The slot is session-scoped, and its injection receives the
1060
+ // session it is scoped to — which is how the dock learns which
1061
+ // conversation a picture belongs to.
1062
+ inject: (sessionId) => ({ t, sessionId }),
1001
1063
  }, GenerateDock));
1002
1064
  // The plugin's own page, keyed by the package name, where the
1003
1065
  // balance and the expiry schedule are spelled out.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-minimaxcode",
3
3
  "displayName": "DSH Connect MiniMax Code",
4
4
  "description": "把本机已登录的 MiniMax Code 桌面 App 模型接入 DeepSeek Harness:零配置、Anthropic Messages 兼容、动态模型目录,并显示积分余额与到期 — bring MiniMax Code desktop app models into DSH with a dynamic model catalog and a credit balance with expiry details.",
5
- "version": "0.6.1",
5
+ "version": "0.6.2",
6
6
  "author": "JeffTsuiCUG",
7
7
  "contributors": [
8
8
  {