@huanlin/dsh-plugin-android-use 0.1.0 → 0.2.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
@@ -39,8 +39,8 @@ window.__ModuleLoader__.load({
39
39
  * Renders both the pre-tap screenshot (annotated with the tap marker) and
40
40
  * the post-tap screenshot (showing the result after tap) inline, along with
41
41
  * coordinates and action metadata. Images are loaded through the
42
- * `uiConversation.imageUrl` service, which converts the ImageAttachmentRef
43
- * into a session-authorized blob URL.
42
+ * session-authorized `loadImage` loader the toolview owner supplies, which
43
+ * converts the ImageAttachmentRef into a session-authorized blob URL.
44
44
  *
45
45
  * @module @huanlin/dsh-plugin-android-use/client/TapCard
46
46
  */
@@ -65,18 +65,18 @@ window.__ModuleLoader__.load({
65
65
  }
66
66
  }
67
67
  /**
68
- * Resolve an attachmentId to a browser URL through the uiConversation service.
69
- * Caches by attachmentId so re-renders don't re-fetch.
68
+ * Resolve an attachmentId to a browser URL through the session-authorized
69
+ * image loader. Caches by attachmentId so re-renders don't re-fetch.
70
70
  */
71
- function useImageUrl$1(uiConversation, sessionId, attachment) {
71
+ function useImageUrl$1(loadImage, attachment) {
72
72
  const [url, setUrl] = (0, react.useState)(void 0);
73
73
  (0, react.useEffect)(() => {
74
- if (attachment === void 0 || sessionId === void 0) {
74
+ if (attachment === void 0) {
75
75
  setUrl(void 0);
76
76
  return;
77
77
  }
78
78
  let cancelled = false;
79
- uiConversation.imageUrl(sessionId, attachment).then((resolved) => {
79
+ loadImage(attachment).then((resolved) => {
80
80
  if (!cancelled) setUrl(resolved);
81
81
  }).catch(() => {
82
82
  if (!cancelled) setUrl(void 0);
@@ -84,17 +84,13 @@ window.__ModuleLoader__.load({
84
84
  return () => {
85
85
  cancelled = true;
86
86
  };
87
- }, [
88
- uiConversation,
89
- sessionId,
90
- attachment
91
- ]);
87
+ }, [loadImage, attachment]);
92
88
  return url;
93
89
  }
94
90
  /**
95
91
  * Render one `android_tap` tool call as a card with pre-tap and post-tap screenshots.
96
92
  */
97
- function TapCard({ block, toolName, sessionId, uiConversation }) {
93
+ function TapCard({ block, toolName, loadImage }) {
98
94
  const settled = "kind" in block ? block : void 0;
99
95
  const running = "kind" in block ? void 0 : block;
100
96
  const images = settled !== void 0 ? findImages(settled) : [];
@@ -107,8 +103,8 @@ window.__ModuleLoader__.load({
107
103
  };
108
104
  const preImage = images[0];
109
105
  const postImage = images[1];
110
- const preUrl = useImageUrl$1(uiConversation, sessionId, preImage?.attachment);
111
- const postUrl = useImageUrl$1(uiConversation, sessionId, postImage?.attachment);
106
+ const preUrl = useImageUrl$1(loadImage, preImage?.attachment);
107
+ const postUrl = useImageUrl$1(loadImage, postImage?.attachment);
112
108
  const state = settled === void 0 ? "running" : settled.isError ? "error" : "completed";
113
109
  const title = coords.x !== void 0 && coords.y !== void 0 ? `Tap (${coords.x}, ${coords.y})` : toolName;
114
110
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -208,8 +204,8 @@ window.__ModuleLoader__.load({
208
204
  * ScreenshotCard — the toolcall card for the `android_screenshot` tool.
209
205
  *
210
206
  * Renders the captured screenshot inline. The image is loaded through
211
- * the `uiConversation.imageUrl` service, which converts the ImageAttachmentRef
212
- * into a session-authorized blob URL.
207
+ * the session-authorized `loadImage` loader the toolview owner supplies,
208
+ * which converts the ImageAttachmentRef into a session-authorized blob URL.
213
209
  *
214
210
  * @module @huanlin/dsh-plugin-android-use/client/ScreenshotCard
215
211
  */
@@ -219,15 +215,15 @@ window.__ModuleLoader__.load({
219
215
  function findText(block) {
220
216
  return block.content.find((c) => c.type === "text")?.text;
221
217
  }
222
- function useImageUrl(uiConversation, sessionId, attachment) {
218
+ function useImageUrl(loadImage, attachment) {
223
219
  const [url, setUrl] = (0, react.useState)(void 0);
224
220
  (0, react.useEffect)(() => {
225
- if (attachment === void 0 || sessionId === void 0) {
221
+ if (attachment === void 0) {
226
222
  setUrl(void 0);
227
223
  return;
228
224
  }
229
225
  let cancelled = false;
230
- uiConversation.imageUrl(sessionId, attachment).then((resolved) => {
226
+ loadImage(attachment).then((resolved) => {
231
227
  if (!cancelled) setUrl(resolved);
232
228
  }).catch(() => {
233
229
  if (!cancelled) setUrl(void 0);
@@ -235,21 +231,17 @@ window.__ModuleLoader__.load({
235
231
  return () => {
236
232
  cancelled = true;
237
233
  };
238
- }, [
239
- uiConversation,
240
- sessionId,
241
- attachment
242
- ]);
234
+ }, [loadImage, attachment]);
243
235
  return url;
244
236
  }
245
237
  /**
246
238
  * Render one `android_screenshot` tool call as a card with the screenshot.
247
239
  */
248
- function ScreenshotCard({ block, toolName, sessionId, uiConversation }) {
240
+ function ScreenshotCard({ block, toolName, loadImage }) {
249
241
  const settled = "kind" in block ? block : void 0;
250
242
  const image = settled !== void 0 ? findImage(settled) : void 0;
251
243
  const text = settled !== void 0 ? findText(settled) : void 0;
252
- const imageUrl = useImageUrl(uiConversation, sessionId, image?.attachment);
244
+ const imageUrl = useImageUrl(loadImage, image?.attachment);
253
245
  const state = settled === void 0 ? "running" : settled.isError ? "error" : "completed";
254
246
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
255
247
  className: classMap.card,
@@ -296,24 +288,20 @@ window.__ModuleLoader__.load({
296
288
  }
297
289
  //#endregion
298
290
  //#region src/client/index.ts
299
- /** Required services: slot registry + Conversation image cache. */
300
- const inject = ["slots", "uiConversation"];
291
+ /** Required services: slot registry. */
292
+ const inject = ["slots"];
301
293
  /**
302
294
  * Client plugin body: register the `android_tap` and `android_screenshot` toolview slots.
303
295
  * @param ctx - client root context.
304
296
  */
305
297
  function apply(ctx) {
306
- const tapInjected = () => ({ uiConversation: ctx.uiConversation });
307
298
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
308
299
  name: "tool.call.toolview",
309
- key: "android_tap",
310
- inject: tapInjected
300
+ key: "android_tap"
311
301
  }, TapCard));
312
- const screenshotInjected = () => ({ uiConversation: ctx.uiConversation });
313
302
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
314
303
  name: "tool.call.toolview",
315
- key: "android_screenshot",
316
- inject: screenshotInjected
304
+ key: "android_screenshot"
317
305
  }, ScreenshotCard));
318
306
  }
319
307
  //#endregion
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["findText","useImageUrl","useState","css","useState","css"],"sources":["../src/client/TapCard.tsx","../src/client/ScreenshotCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * TapCard — the toolcall card for the `android_tap` tool.\n *\n * Renders both the pre-tap screenshot (annotated with the tap marker) and\n * the post-tap screenshot (showing the result after tap) inline, along with\n * coordinates and action metadata. Images are loaded through the\n * `uiConversation.imageUrl` service, which converts the ImageAttachmentRef\n * into a session-authorized blob URL.\n *\n * @module @huanlin/dsh-plugin-android-use/client/TapCard\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ToolResultNode, UiConversation } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { ContentBlock, ImageBlock } from '@deepseek-ai/dsh-llm'\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport css from './TapCard.module.css'\n\n/** Inject face: the Conversation image cache for durable URL resolution. */\nexport type TapCardInjected = {\n uiConversation: UiConversation\n}\n\ntype TapCardProps = ToolCallViewProps & InjectFace<TapCardInjected>\n\n/** Extract all image blocks from a settled tool result, in order. */\nfunction findImages(block: ToolResultNode): ImageBlock[] {\n return block.content.filter((c): c is ImageBlock => c.type === 'image')\n}\n\n/** Extract the first text block (summary) from a settled tool result. */\nfunction findText(block: ToolResultNode): string | undefined {\n const text = block.content.find((c): c is Extract<ContentBlock, { type: 'text' }> => c.type === 'text')\n return text?.text\n}\n\n/** Parse tap coordinates from the running block's argsRaw. */\nfunction parseTapArgs(argsRaw: string): { x?: number; y?: number } {\n try {\n const parsed = JSON.parse(argsRaw) as { x?: unknown; y?: unknown }\n return {\n x: typeof parsed.x === 'number' ? parsed.x : undefined,\n y: typeof parsed.y === 'number' ? parsed.y : undefined,\n }\n } catch {\n return {}\n }\n}\n\n/**\n * Resolve an attachmentId to a browser URL through the uiConversation service.\n * Caches by attachmentId so re-renders don't re-fetch.\n */\nfunction useImageUrl(\n uiConversation: UiConversation,\n sessionId: TapCardProps['sessionId'] | undefined,\n attachment: ImageAttachmentRef | undefined,\n): string | undefined {\n const [url, setUrl] = useState<string | undefined>(undefined)\n useEffect(() => {\n if (attachment === undefined || sessionId === undefined) {\n setUrl(undefined)\n return\n }\n let cancelled = false\n uiConversation.imageUrl(sessionId, attachment)\n .then((resolved) => { if (!cancelled) setUrl(resolved) })\n .catch(() => { if (!cancelled) setUrl(undefined) })\n return () => { cancelled = true }\n }, [uiConversation, sessionId, attachment])\n return url\n}\n\n/**\n * Render one `android_tap` tool call as a card with pre-tap and post-tap screenshots.\n */\nexport function TapCard({ block, toolName, sessionId, uiConversation }: TapCardProps) {\n const settled = 'kind' in block ? block : undefined\n const running = 'kind' in block ? undefined : block\n\n const images = settled !== undefined ? findImages(settled) : []\n const text = settled !== undefined ? findText(settled) : undefined\n const runArgs = running !== undefined ? parseTapArgs(running.argsRaw) : undefined\n const settledArgs = settled?.call != null ? parseTapArgs(settled.call.argsRaw) : undefined\n const coords = runArgs ?? settledArgs ?? { x: undefined, y: undefined }\n\n const preImage = images[0]\n const postImage = images[1]\n const preUrl = useImageUrl(uiConversation, sessionId, preImage?.attachment)\n const postUrl = useImageUrl(uiConversation, sessionId, postImage?.attachment)\n\n const state = settled === undefined ? 'running' : (settled.isError ? 'error' : 'completed')\n\n const title = coords.x !== undefined && coords.y !== undefined\n ? `Tap (${coords.x}, ${coords.y})`\n : toolName\n\n return (\n <div className={css.card} data-tool={toolName} data-state={state}>\n <div className={css.header}>\n <span className={css.stateDot} aria-hidden />\n <span className={css.title}>{title}</span>\n <span className={css.badge}>{state}</span>\n </div>\n <div className={css.imageGrid}>\n <div className={css.imageSlot}>\n <span className={css.imageLabel}>Pre-tap (annotated)</span>\n <div className={css.imageWrap}>\n {preUrl !== undefined ? (\n <img className={css.image} src={preUrl} alt={`Pre-tap screenshot at (${coords.x ?? '?'}, ${coords.y ?? '?'})`} />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : preImage !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n </div>\n <div className={css.imageSlot}>\n <span className={css.imageLabel}>Post-tap (result)</span>\n <div className={css.imageWrap}>\n {postUrl !== undefined ? (\n <img className={css.image} src={postUrl} alt=\"Post-tap screenshot showing the result\" />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : postImage !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n </div>\n </div>\n {text !== undefined && (\n <div className={css.meta}>\n {text.split('\\n').map((line, i) => (\n <span key={i} className={css.metaItem}>{line}</span>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ScreenshotCard — the toolcall card for the `android_screenshot` tool.\n *\n * Renders the captured screenshot inline. The image is loaded through\n * the `uiConversation.imageUrl` service, which converts the ImageAttachmentRef\n * into a session-authorized blob URL.\n *\n * @module @huanlin/dsh-plugin-android-use/client/ScreenshotCard\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ToolResultNode, UiConversation } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { ContentBlock, ImageBlock } from '@deepseek-ai/dsh-llm'\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport css from './ScreenshotCard.module.css'\n\n/** Inject face: the Conversation image cache for durable URL resolution. */\nexport type ScreenshotCardInjected = {\n uiConversation: UiConversation\n}\n\ntype ScreenshotCardProps = ToolCallViewProps & InjectFace<ScreenshotCardInjected>\n\nfunction findImage(block: ToolResultNode): ImageBlock | undefined {\n return block.content.find((c): c is ImageBlock => c.type === 'image')\n}\n\nfunction findText(block: ToolResultNode): string | undefined {\n const text = block.content.find((c): c is Extract<ContentBlock, { type: 'text' }> => c.type === 'text')\n return text?.text\n}\n\nfunction useImageUrl(\n uiConversation: UiConversation,\n sessionId: ScreenshotCardProps['sessionId'] | undefined,\n attachment: ImageAttachmentRef | undefined,\n): string | undefined {\n const [url, setUrl] = useState<string | undefined>(undefined)\n useEffect(() => {\n if (attachment === undefined || sessionId === undefined) {\n setUrl(undefined)\n return\n }\n let cancelled = false\n uiConversation.imageUrl(sessionId, attachment)\n .then((resolved) => { if (!cancelled) setUrl(resolved) })\n .catch(() => { if (!cancelled) setUrl(undefined) })\n return () => { cancelled = true }\n }, [uiConversation, sessionId, attachment])\n return url\n}\n\n/**\n * Render one `android_screenshot` tool call as a card with the screenshot.\n */\nexport function ScreenshotCard({ block, toolName, sessionId, uiConversation }: ScreenshotCardProps) {\n const settled = 'kind' in block ? block : undefined\n\n const image = settled !== undefined ? findImage(settled) : undefined\n const text = settled !== undefined ? findText(settled) : undefined\n const imageUrl = useImageUrl(uiConversation, sessionId, image?.attachment)\n\n const state = settled === undefined ? 'running' : (settled.isError ? 'error' : 'completed')\n\n return (\n <div className={css.card} data-tool={toolName} data-state={state}>\n <div className={css.header}>\n <span className={css.stateDot} aria-hidden />\n <span className={css.title}>{toolName}</span>\n <span className={css.badge}>{state}</span>\n </div>\n <div className={css.imageWrap}>\n {imageUrl !== undefined ? (\n <img className={css.image} src={imageUrl} alt=\"Android screenshot\" />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : image !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n {text !== undefined && (\n <div className={css.meta}>\n {text.split('\\n').map((line, i) => (\n <span key={i} className={css.metaItem}>{line}</span>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * dsh-plugin-android-use — browser half.\n *\n * Single bundle, dual entry: this is the client half (exports `./client`).\n * Host half ships via `.` (see `src/index.ts`).\n *\n * Registers two `tool.call.toolview` keyed slots:\n * - key `android_tap` → TapCard (annotated pre-tap screenshot)\n * - key `android_screenshot` → ScreenshotCard (captured screenshot)\n *\n * `tool.call.toolview` is declared by `@deepseek-ai/dsh-client-ui-tool`, so\n * both registrations go through `ctx.slots.inject` (waits on the declaration,\n * leaves with this plugin's fiber). Durable image URLs resolve through the\n * `uiConversation` service (v0.1.2: `conversation.resolveImage` is gone).\n *\n * @module @huanlin/dsh-plugin-android-use/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only imports: pull the Context/SlotMap/StandardProps declaration\n// merges this plugin's compiled props depend on. Erased in the bundle.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { TapCard, type TapCardInjected } from './TapCard.tsx'\nimport { ScreenshotCard, type ScreenshotCardInjected } from './ScreenshotCard.tsx'\n\n/** Required services: slot registry + Conversation image cache. */\nexport const inject = ['slots', 'uiConversation']\n\n/**\n * Client plugin body: register the `android_tap` and `android_screenshot` toolview slots.\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n const tapInjected = (): TapCardInjected => ({ uiConversation: ctx.uiConversation })\n ctx.slots.inject('tool.call.toolview', () =>\n ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'android_tap',\n inject: tapInjected,\n }, TapCard))\n\n const screenshotInjected = (): ScreenshotCardInjected => ({ uiConversation: ctx.uiConversation })\n ctx.slots.inject('tool.call.toolview', () =>\n ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'android_screenshot',\n inject: screenshotInjected,\n }, ScreenshotCard))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["findText","useImageUrl","useState","css","useState","css"],"sources":["../src/client/TapCard.tsx","../src/client/ScreenshotCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * TapCard — the toolcall card for the `android_tap` tool.\n *\n * Renders both the pre-tap screenshot (annotated with the tap marker) and\n * the post-tap screenshot (showing the result after tap) inline, along with\n * coordinates and action metadata. Images are loaded through the\n * session-authorized `loadImage` loader the toolview owner supplies, which\n * converts the ImageAttachmentRef into a session-authorized blob URL.\n *\n * @module @huanlin/dsh-plugin-android-use/client/TapCard\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { MessageImageLoader, ToolResultNode } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { ContentBlock, ImageBlock } from '@deepseek-ai/dsh-llm'\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport css from './TapCard.module.css'\n\ntype TapCardProps = ToolCallViewProps\n\n/** Extract all image blocks from a settled tool result, in order. */\nfunction findImages(block: ToolResultNode): ImageBlock[] {\n return block.content.filter((c): c is ImageBlock => c.type === 'image')\n}\n\n/** Extract the first text block (summary) from a settled tool result. */\nfunction findText(block: ToolResultNode): string | undefined {\n const text = block.content.find((c): c is Extract<ContentBlock, { type: 'text' }> => c.type === 'text')\n return text?.text\n}\n\n/** Parse tap coordinates from the running block's argsRaw. */\nfunction parseTapArgs(argsRaw: string): { x?: number; y?: number } {\n try {\n const parsed = JSON.parse(argsRaw) as { x?: unknown; y?: unknown }\n return {\n x: typeof parsed.x === 'number' ? parsed.x : undefined,\n y: typeof parsed.y === 'number' ? parsed.y : undefined,\n }\n } catch {\n return {}\n }\n}\n\n/**\n * Resolve an attachmentId to a browser URL through the session-authorized\n * image loader. Caches by attachmentId so re-renders don't re-fetch.\n */\nfunction useImageUrl(\n loadImage: MessageImageLoader,\n attachment: ImageAttachmentRef | undefined,\n): string | undefined {\n const [url, setUrl] = useState<string | undefined>(undefined)\n useEffect(() => {\n if (attachment === undefined) {\n setUrl(undefined)\n return\n }\n let cancelled = false\n loadImage(attachment)\n .then((resolved) => { if (!cancelled) setUrl(resolved) })\n .catch(() => { if (!cancelled) setUrl(undefined) })\n return () => { cancelled = true }\n }, [loadImage, attachment])\n return url\n}\n\n/**\n * Render one `android_tap` tool call as a card with pre-tap and post-tap screenshots.\n */\nexport function TapCard({ block, toolName, loadImage }: TapCardProps) {\n const settled = 'kind' in block ? block : undefined\n const running = 'kind' in block ? undefined : block\n\n const images = settled !== undefined ? findImages(settled) : []\n const text = settled !== undefined ? findText(settled) : undefined\n const runArgs = running !== undefined ? parseTapArgs(running.argsRaw) : undefined\n const settledArgs = settled?.call != null ? parseTapArgs(settled.call.argsRaw) : undefined\n const coords = runArgs ?? settledArgs ?? { x: undefined, y: undefined }\n\n const preImage = images[0]\n const postImage = images[1]\n const preUrl = useImageUrl(loadImage, preImage?.attachment)\n const postUrl = useImageUrl(loadImage, postImage?.attachment)\n\n const state = settled === undefined ? 'running' : (settled.isError ? 'error' : 'completed')\n\n const title = coords.x !== undefined && coords.y !== undefined\n ? `Tap (${coords.x}, ${coords.y})`\n : toolName\n\n return (\n <div className={css.card} data-tool={toolName} data-state={state}>\n <div className={css.header}>\n <span className={css.stateDot} aria-hidden />\n <span className={css.title}>{title}</span>\n <span className={css.badge}>{state}</span>\n </div>\n <div className={css.imageGrid}>\n <div className={css.imageSlot}>\n <span className={css.imageLabel}>Pre-tap (annotated)</span>\n <div className={css.imageWrap}>\n {preUrl !== undefined ? (\n <img className={css.image} src={preUrl} alt={`Pre-tap screenshot at (${coords.x ?? '?'}, ${coords.y ?? '?'})`} />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : preImage !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n </div>\n <div className={css.imageSlot}>\n <span className={css.imageLabel}>Post-tap (result)</span>\n <div className={css.imageWrap}>\n {postUrl !== undefined ? (\n <img className={css.image} src={postUrl} alt=\"Post-tap screenshot showing the result\" />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : postImage !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n </div>\n </div>\n {text !== undefined && (\n <div className={css.meta}>\n {text.split('\\n').map((line, i) => (\n <span key={i} className={css.metaItem}>{line}</span>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ScreenshotCard — the toolcall card for the `android_screenshot` tool.\n *\n * Renders the captured screenshot inline. The image is loaded through\n * the session-authorized `loadImage` loader the toolview owner supplies,\n * which converts the ImageAttachmentRef into a session-authorized blob URL.\n *\n * @module @huanlin/dsh-plugin-android-use/client/ScreenshotCard\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { MessageImageLoader, ToolResultNode } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { ContentBlock, ImageBlock } from '@deepseek-ai/dsh-llm'\nimport type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\nimport css from './ScreenshotCard.module.css'\n\ntype ScreenshotCardProps = ToolCallViewProps\n\nfunction findImage(block: ToolResultNode): ImageBlock | undefined {\n return block.content.find((c): c is ImageBlock => c.type === 'image')\n}\n\nfunction findText(block: ToolResultNode): string | undefined {\n const text = block.content.find((c): c is Extract<ContentBlock, { type: 'text' }> => c.type === 'text')\n return text?.text\n}\n\nfunction useImageUrl(\n loadImage: MessageImageLoader,\n attachment: ImageAttachmentRef | undefined,\n): string | undefined {\n const [url, setUrl] = useState<string | undefined>(undefined)\n useEffect(() => {\n if (attachment === undefined) {\n setUrl(undefined)\n return\n }\n let cancelled = false\n loadImage(attachment)\n .then((resolved) => { if (!cancelled) setUrl(resolved) })\n .catch(() => { if (!cancelled) setUrl(undefined) })\n return () => { cancelled = true }\n }, [loadImage, attachment])\n return url\n}\n\n/**\n * Render one `android_screenshot` tool call as a card with the screenshot.\n */\nexport function ScreenshotCard({ block, toolName, loadImage }: ScreenshotCardProps) {\n const settled = 'kind' in block ? block : undefined\n\n const image = settled !== undefined ? findImage(settled) : undefined\n const text = settled !== undefined ? findText(settled) : undefined\n const imageUrl = useImageUrl(loadImage, image?.attachment)\n\n const state = settled === undefined ? 'running' : (settled.isError ? 'error' : 'completed')\n\n return (\n <div className={css.card} data-tool={toolName} data-state={state}>\n <div className={css.header}>\n <span className={css.stateDot} aria-hidden />\n <span className={css.title}>{toolName}</span>\n <span className={css.badge}>{state}</span>\n </div>\n <div className={css.imageWrap}>\n {imageUrl !== undefined ? (\n <img className={css.image} src={imageUrl} alt=\"Android screenshot\" />\n ) : (\n <span className={css.placeholder}>\n {settled === undefined ? 'Capturing…' : image !== undefined ? 'Loading…' : 'No screenshot'}\n </span>\n )}\n </div>\n {text !== undefined && (\n <div className={css.meta}>\n {text.split('\\n').map((line, i) => (\n <span key={i} className={css.metaItem}>{line}</span>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * dsh-plugin-android-use — browser half.\n *\n * Single bundle, dual entry: this is the client half (exports `./client`).\n * Host half ships via `.` (see `src/index.ts`).\n *\n * Registers two `tool.call.toolview` keyed slots:\n * - key `android_tap` → TapCard (annotated pre-tap screenshot)\n * - key `android_screenshot` → ScreenshotCard (captured screenshot)\n *\n * `tool.call.toolview` is declared by `@deepseek-ai/dsh-client-ui-tool`, so\n * both registrations go through `ctx.slots.inject` (waits on the declaration,\n * leaves with this plugin's fiber). Durable image URLs resolve through the\n * session-authorized `loadImage` loader the toolview owner supplies\n * (v0.1.5: `ToolCallOwnerProps.loadImage` is required).\n *\n * @module @huanlin/dsh-plugin-android-use/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only imports: pull the Context/SlotMap/StandardProps declaration\n// merges this plugin's compiled props depend on. Erased in the bundle.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { TapCard } from './TapCard.tsx'\nimport { ScreenshotCard } from './ScreenshotCard.tsx'\n\n/** Required services: slot registry. */\nexport const inject = ['slots']\n\n/**\n * Client plugin body: register the `android_tap` and `android_screenshot` toolview slots.\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n ctx.slots.inject('tool.call.toolview', () =>\n ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'android_tap',\n }, TapCard))\n\n ctx.slots.inject('tool.call.toolview', () =>\n ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'android_screenshot',\n }, ScreenshotCard))\n}\n"],"mappings":"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"}
package/lib/index.js CHANGED
@@ -712,7 +712,7 @@ async function fitToMaxDimension(data, maxDimension) {
712
712
  function renderScreenshot(_args, value) {
713
713
  const v = value;
714
714
  const text = [
715
- `Screenshot captured: ${v.width}x${v.height} px, ${v.bytes} bytes (image/png).`,
715
+ `Screenshot captured: ${v.width}x${v.height} px, ${v.bytes} bytes (${v.image.mediaType}).`,
716
716
  `Device resolution: ${v.device_width}x${v.device_height}, scale: ${v.scale.toFixed(4)}.`,
717
717
  `Coordinates from android_ui_dump and android_tap use the ${v.width}x${v.height} image space.`,
718
718
  `Image emitted to model context: ${v.image_emitted ? "yes" : "no"}${v.image_emitted ? "" : " (current route is not image-capable; use android_ui_dump for screen perception)"}.`
@@ -720,7 +720,7 @@ function renderScreenshot(_args, value) {
720
720
  if (v.image_emitted) {
721
721
  const ref = {
722
722
  attachmentId: v.image.attachmentId,
723
- mediaType: "image/png",
723
+ mediaType: v.image.mediaType,
724
724
  bytes: v.image.bytes,
725
725
  width: v.image.width,
726
726
  height: v.image.height
@@ -854,7 +854,6 @@ function registerScreenTools(ctx, deps) {
854
854
  },
855
855
  mediaType: {
856
856
  type: "string",
857
- const: "image/png",
858
857
  required: true
859
858
  },
860
859
  bytes: {
@@ -904,7 +903,7 @@ function registerScreenTools(ctx, deps) {
904
903
  scale,
905
904
  image: {
906
905
  attachmentId: ref.attachmentId,
907
- mediaType: "image/png",
906
+ mediaType: ref.mediaType,
908
907
  bytes: ref.bytes,
909
908
  width: ref.width,
910
909
  height: ref.height
@@ -1472,7 +1471,7 @@ function renderTap(_args, value) {
1472
1471
  type: "image",
1473
1472
  attachment: {
1474
1473
  attachmentId: v.pre_tap_screenshot.attachmentId,
1475
- mediaType: "image/png",
1474
+ mediaType: v.pre_tap_screenshot.mediaType ?? "image/png",
1476
1475
  bytes: v.pre_tap_screenshot.bytes,
1477
1476
  width: v.pre_tap_screenshot.width,
1478
1477
  height: v.pre_tap_screenshot.height
@@ -1488,7 +1487,7 @@ function renderTap(_args, value) {
1488
1487
  type: "image",
1489
1488
  attachment: {
1490
1489
  attachmentId: v.post_tap_screenshot.attachmentId,
1491
- mediaType: "image/png",
1490
+ mediaType: v.post_tap_screenshot.mediaType ?? "image/png",
1492
1491
  bytes: v.post_tap_screenshot.bytes,
1493
1492
  width: v.post_tap_screenshot.width,
1494
1493
  height: v.post_tap_screenshot.height
@@ -1591,6 +1590,10 @@ function registerInputTools(ctx, deps) {
1591
1590
  type: "string",
1592
1591
  required: true
1593
1592
  },
1593
+ mediaType: {
1594
+ type: "string",
1595
+ required: true
1596
+ },
1594
1597
  bytes: {
1595
1598
  type: "integer",
1596
1599
  required: true
@@ -1616,6 +1619,10 @@ function registerInputTools(ctx, deps) {
1616
1619
  type: "string",
1617
1620
  required: true
1618
1621
  },
1622
+ mediaType: {
1623
+ type: "string",
1624
+ required: true
1625
+ },
1619
1626
  bytes: {
1620
1627
  type: "integer",
1621
1628
  required: true
@@ -1666,6 +1673,7 @@ function registerInputTools(ctx, deps) {
1666
1673
  });
1667
1674
  preScreenshot = {
1668
1675
  attachmentId: ref.attachmentId,
1676
+ mediaType: ref.mediaType,
1669
1677
  bytes: ref.bytes,
1670
1678
  width: ref.width,
1671
1679
  height: ref.height
@@ -1686,6 +1694,7 @@ function registerInputTools(ctx, deps) {
1686
1694
  });
1687
1695
  postScreenshot = {
1688
1696
  attachmentId: ref.attachmentId,
1697
+ mediaType: ref.mediaType,
1689
1698
  bytes: ref.bytes,
1690
1699
  width: ref.width,
1691
1700
  height: ref.height
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huanlin/dsh-plugin-android-use",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -50,14 +50,14 @@
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@deepseek-ai/cordis": "^4.0.1",
53
- "@deepseek-ai/dsh-attachment": "^0.1.2-rc.1",
54
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
55
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
56
- "@deepseek-ai/dsh-client-ui-session": "^0.1.2-rc.1",
57
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
58
- "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-rc.1",
59
- "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
60
- "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
53
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.1",
54
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.1",
55
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
56
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.1",
57
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
58
+ "@deepseek-ai/dsh-client-ui-tool": "^0.1.5-rc.1",
59
+ "@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
60
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
61
61
  "react": "^18.2.0"
62
62
  },
63
63
  "peerDependenciesMeta": {
@@ -93,6 +93,16 @@
93
93
  }
94
94
  },
95
95
  "devDependencies": {
96
+ "@deepseek-ai/cordis": "^4.0.1",
97
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.1",
98
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.1",
99
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
100
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.1",
101
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
102
+ "@deepseek-ai/dsh-client-ui-tool": "^0.1.5-rc.1",
103
+ "@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
104
+ "@deepseek-ai/dsh-scope": "^0.1.5-rc.1",
105
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
96
106
  "@types/node": "^22.20.0",
97
107
  "@types/react": "^18.3.0",
98
108
  "react": "^18.2.0",