@huanlin/dsh-plugin-android-use 0.1.0 → 0.2.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/lib/client.js +23 -35
- package/lib/client.js.map +1 -1
- package/lib/index.js +15 -6
- package/package.json +19 -9
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
|
-
* `
|
|
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
|
|
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(
|
|
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
|
|
74
|
+
if (attachment === void 0) {
|
|
75
75
|
setUrl(void 0);
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
let cancelled = false;
|
|
79
|
-
|
|
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,
|
|
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(
|
|
111
|
-
const postUrl = useImageUrl$1(
|
|
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 `
|
|
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(
|
|
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
|
|
221
|
+
if (attachment === void 0) {
|
|
226
222
|
setUrl(void 0);
|
|
227
223
|
return;
|
|
228
224
|
}
|
|
229
225
|
let cancelled = false;
|
|
230
|
-
|
|
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,
|
|
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(
|
|
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
|
|
300
|
-
const inject = ["slots"
|
|
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
|
|
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:
|
|
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:
|
|
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.
|
|
3
|
+
"version": "0.2.0",
|
|
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.
|
|
54
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-session": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-tool": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
60
|
-
"@deepseek-ai/dsh-tools": "^0.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",
|