@opentiny/next-sdk 0.2.8 → 0.2.10

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.
@@ -27073,27 +27073,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
27073
27073
  }
27074
27074
  /**
27075
27075
  * 合并菜单项配置。
27076
- * - sessionId:使用默认菜单 + 用户配置(可定制每一项的 show/text/icon 等)
27077
- * - sessionId:不渲染任何下拉菜单,仅保留点击浮标打开对话框的能力
27076
+ * - 用户明确传入 menuItems:直接使用用户配置,不受 sessionId 限制;未传 icon 时自动补充默认图标
27077
+ * - sessionId 且未传 menuItems:使用默认菜单
27078
+ * - 无 sessionId 且未传 menuItems:不渲染任何下拉菜单,仅保留点击浮标打开对话框的能力
27078
27079
  */
27079
27080
  mergeMenuItems(userMenuItems) {
27081
+ const defaultIcons = {
27082
+ "qr-code": qrCode,
27083
+ "ai-chat": chat,
27084
+ "remote-url": link,
27085
+ "remote-control": scan
27086
+ };
27087
+ if (userMenuItems) {
27088
+ return userMenuItems.map((item) => ({
27089
+ ...item,
27090
+ icon: item.icon ?? defaultIcons[item.action]
27091
+ }));
27092
+ }
27080
27093
  if (!this.options.sessionId) {
27081
27094
  return [];
27082
27095
  }
27083
- if (!userMenuItems) {
27084
- return getDefaultMenuItems(this.options);
27085
- }
27086
- return getDefaultMenuItems(this.options).map((defaultItem) => {
27087
- const userItem = userMenuItems.find((item) => item.action === defaultItem.action);
27088
- if (userItem) {
27089
- return {
27090
- ...defaultItem,
27091
- ...userItem,
27092
- show: userItem.show !== void 0 ? userItem.show : defaultItem.show
27093
- };
27094
- }
27095
- return defaultItem;
27096
- });
27096
+ return getDefaultMenuItems(this.options);
27097
27097
  }
27098
27098
  init() {
27099
27099
  this.createFloatingBlock();
@@ -27124,7 +27124,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
27124
27124
  <div class="tiny-remoter-dropdown-item__content">
27125
27125
  <div title="${item.tip}">${item.text}</div>
27126
27126
  <div class="tiny-remoter-dropdown-item__desc-wrapper">
27127
- <div class="tiny-remoter-dropdown-item__desc ${item.active ? "tiny-remoter-dropdown-item__desc--active" : ""} ${item.know ? "tiny-remoter-dropdown-item__desc--know" : ""}">${item.desc}</div>
27127
+ <div class="tiny-remoter-dropdown-item__desc ${item.active ? "tiny-remoter-dropdown-item__desc--active" : ""} ${item.know ? "tiny-remoter-dropdown-item__desc--know" : ""}">${item.desc ?? ""}</div>
27128
27128
  <div>
27129
27129
  ${item.showCopyIcon ? `
27130
27130
  <div class="tiny-remoter-copy-icon" id="${item.action}" data-action="${item.action}">
@@ -27232,12 +27232,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
27232
27232
  this.closeDropdown();
27233
27233
  }
27234
27234
  copyRemoteControl() {
27235
- if (!this.options.sessionId) return;
27236
- this.copyToClipboard(this.options.sessionId.slice(-6));
27235
+ const menuItem = this.menuItems.find((item) => item.action === "remote-control");
27236
+ const codeToCopy = menuItem?.desc || menuItem?.text || (this.options.sessionId ? this.options.sessionId.slice(-6) : "");
27237
+ if (codeToCopy) {
27238
+ this.copyToClipboard(codeToCopy);
27239
+ }
27237
27240
  }
27238
27241
  copyRemoteURL() {
27239
- if (!this.options.sessionId) return;
27240
- this.copyToClipboard(this.options.remoteUrl + this.sessionPrefix + this.options.sessionId);
27242
+ const menuItem = this.menuItems.find((item) => item.action === "remote-url");
27243
+ const sessionUrl = this.options.sessionId ? this.options.remoteUrl + this.sessionPrefix + this.options.sessionId : "";
27244
+ const customDesc = menuItem?.desc && menuItem.desc !== this.options.remoteUrl ? menuItem.desc : void 0;
27245
+ const urlToCopy = customDesc || sessionUrl || menuItem?.text || "";
27246
+ if (urlToCopy) {
27247
+ this.copyToClipboard(urlToCopy);
27248
+ }
27241
27249
  }
27242
27250
  // 实现复制到剪贴板功能
27243
27251
  async copyToClipboard(text2) {
@@ -50022,7 +50030,13 @@ Thought: 用户想要获取今天的日期,我需要调用日期相关的工
50022
50030
  try {
50023
50031
  let transport;
50024
50032
  if ("type" in serverConfig && serverConfig.type.toLocaleLowerCase() === "streamablehttp") {
50025
- transport = new StreamableHTTPClientTransport(new URL(serverConfig.url));
50033
+ const configWithHeaders = serverConfig;
50034
+ const requestInit = configWithHeaders.headers ? { headers: configWithHeaders.headers } : void 0;
50035
+ transport = new StreamableHTTPClientTransport(new URL(configWithHeaders.url), { requestInit });
50036
+ } else if ("type" in serverConfig && serverConfig.type === "sse") {
50037
+ const configWithHeaders = serverConfig;
50038
+ const requestInit = configWithHeaders.headers ? { headers: configWithHeaders.headers } : void 0;
50039
+ transport = new SSEClientTransport(new URL(configWithHeaders.url), { requestInit });
50026
50040
  } else if ("type" in serverConfig && serverConfig.type === "extension") {
50027
50041
  transport = new ExtensionClientTransport(serverConfig.sessionId);
50028
50042
  } else if ("transport" in serverConfig) {
@@ -50859,6 +50873,7 @@ ${observationText}
50859
50873
  const MSG_REMOTER_READY = "next-sdk:remoter-ready";
50860
50874
  const MSG_ROUTE_STATE_INITIAL = "next-sdk:route-state-initial";
50861
50875
  const activePages = /* @__PURE__ */ new Map();
50876
+ const normalizeRoute = (value) => value.replace(/\/+$/, "") || "/";
50862
50877
  const broadcastTargets = /* @__PURE__ */ new Set();
50863
50878
  function initBroadcastTargets() {
50864
50879
  if (typeof window !== "undefined") {
@@ -50905,6 +50920,88 @@ ${observationText}
50905
50920
  function setNavigator(fn) {
50906
50921
  _navigator = fn;
50907
50922
  }
50923
+ function waitForPageReady(path, timeoutMs = 1500) {
50924
+ if (typeof window === "undefined") {
50925
+ return Promise.resolve();
50926
+ }
50927
+ const target = normalizeRoute(path);
50928
+ return new Promise((resolve2) => {
50929
+ let done = false;
50930
+ const cleanup = () => {
50931
+ if (done) return;
50932
+ done = true;
50933
+ window.removeEventListener("message", handleMessage);
50934
+ resolve2();
50935
+ };
50936
+ const handleMessage = (event) => {
50937
+ if (event.source !== window || event.data?.type !== MSG_PAGE_READY) return;
50938
+ const route = normalizeRoute(String(event.data.route ?? ""));
50939
+ if (route === target) {
50940
+ cleanup();
50941
+ }
50942
+ };
50943
+ window.addEventListener("message", handleMessage);
50944
+ setTimeout(cleanup, timeoutMs);
50945
+ });
50946
+ }
50947
+ function registerNavigateTool(server, options) {
50948
+ const name16 = options?.name ?? "navigate_to_page";
50949
+ const title2 = options?.title ?? "页面跳转";
50950
+ const description2 = options?.description ?? '当需要的工具在当前页面不可用时,使用此工具跳转到特定页面。例如:要查询订单时跳转到 "/orders",要创建价保时跳转到 "/price-protection"。';
50951
+ const timeoutMs = options?.timeoutMs ?? 1500;
50952
+ return server.registerTool(
50953
+ name16,
50954
+ {
50955
+ title: title2,
50956
+ description: description2,
50957
+ inputSchema: {
50958
+ path: stringType().describe('目标页面的路由地址,例如 "/orders"、"/inventory"、"/price-protection" 等。')
50959
+ }
50960
+ },
50961
+ async ({ path }) => {
50962
+ if (typeof window === "undefined") {
50963
+ return {
50964
+ content: [{ type: "text", text: "当前环境不支持页面跳转(window 不存在)。" }]
50965
+ };
50966
+ }
50967
+ if (!_navigator) {
50968
+ return {
50969
+ content: [
50970
+ {
50971
+ type: "text",
50972
+ text: "页面跳转失败:尚未在应用入口调用 setNavigator 注册导航函数,无法执行路由跳转。"
50973
+ }
50974
+ ]
50975
+ };
50976
+ }
50977
+ try {
50978
+ const target = normalizeRoute(path);
50979
+ const current = normalizeRoute(window.location.pathname);
50980
+ const isAlreadyOnTarget = current === target || current.endsWith(target) && (current.length === target.length || current[current.lastIndexOf(target) - 1] === "/");
50981
+ if (isAlreadyOnTarget) {
50982
+ return {
50983
+ content: [{ type: "text", text: `当前已在页面:${path}。请继续你的下一步操作。` }]
50984
+ };
50985
+ }
50986
+ const readyPromise = waitForPageReady(path, timeoutMs);
50987
+ await _navigator(path);
50988
+ await readyPromise;
50989
+ return {
50990
+ content: [{ type: "text", text: `已成功跳转至页面:${path}。请继续你的下一步操作。` }]
50991
+ };
50992
+ } catch (err) {
50993
+ return {
50994
+ content: [
50995
+ {
50996
+ type: "text",
50997
+ text: `页面跳转失败:${err instanceof Error ? err.message : String(err)}。`
50998
+ }
50999
+ ]
51000
+ };
51001
+ }
51002
+ }
51003
+ );
51004
+ }
50908
51005
  function buildPageHandler(name16, route, timeout = 3e4, effectConfig) {
50909
51006
  return (input) => {
50910
51007
  const callId = randomUUID();
@@ -50994,10 +51091,10 @@ ${observationText}
50994
51091
  }
50995
51092
  function registerPageTool(options) {
50996
51093
  const { route: routeOption, handlers } = options;
50997
- const normalizeRoute = (value) => value.replace(/\/+$/, "") || "/";
50998
- const route = normalizeRoute(routeOption ?? window.location.pathname);
51094
+ const normalizeRoute2 = (value) => value.replace(/\/+$/, "") || "/";
51095
+ const route = normalizeRoute2(routeOption ?? window.location.pathname);
50999
51096
  const handleMessage = async (event) => {
51000
- if (event.source !== window || event.data?.type !== MSG_TOOL_CALL || normalizeRoute(String(event.data?.route ?? "")) !== route || !(event.data.toolName in handlers)) {
51097
+ if (event.source !== window || event.data?.type !== MSG_TOOL_CALL || normalizeRoute2(String(event.data?.route ?? "")) !== route || !(event.data.toolName in handlers)) {
51001
51098
  return;
51002
51099
  }
51003
51100
  const { callId, toolName, input } = event.data;
@@ -51218,6 +51315,7 @@ ${lines.join("\n")}
51218
51315
  exports2.isSSEClientTransport = isSSEClientTransport;
51219
51316
  exports2.isStreamableHTTPClientTransport = isStreamableHTTPClientTransport;
51220
51317
  exports2.parseSkillFrontMatter = parseSkillFrontMatter;
51318
+ exports2.registerNavigateTool = registerNavigateTool;
51221
51319
  exports2.registerPageTool = registerPageTool;
51222
51320
  exports2.setNavigator = setNavigator;
51223
51321
  exports2.withPageTools = withPageTools;