dsh-mobile 0.4.7 → 0.5.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
@@ -513,6 +513,30 @@ window.__ModuleLoader__.load({
513
513
  wsPathsAnnounce: "{n} 条插件连接等待放行",
514
514
  wsPathsAdvancedHint: "通常不需要手动填写:用上方按钮即可。只有当你确切知道所需路径时才填。"
515
515
  });
516
+ Object.assign(MOBILE_CONTROL_MESSAGES.en, {
517
+ cpolarDownloadFailed: "Could not download cpolar from its official site. Check this computer’s network or proxy, then retry.",
518
+ cpolarDownloadTimeout: "The cpolar download timed out. Check this computer’s network or proxy, then retry.",
519
+ cpolarDownloadHttp: "The official cpolar download returned HTTP {status}. Retry later or check the official download page.",
520
+ cpolarIntegrityFailed: "The cpolar download did not match the pinned build, so it was not installed. Retry later or update DSH Mobile; do not bypass verification.",
521
+ cpolarExtractFailed: "The cpolar download completed, but extraction failed. Check system extraction tools and security software, then retry.",
522
+ cpolarStorageFailed: "Could not write the cpolar component in DSH Mobile’s private directory. Check disk space, permissions, and security software, then retry."
523
+ });
524
+ Object.assign(MOBILE_CONTROL_MESSAGES.it, {
525
+ cpolarDownloadFailed: "Impossibile scaricare cpolar dal sito ufficiale. Controlla rete o proxy del computer e riprova.",
526
+ cpolarDownloadTimeout: "Download di cpolar scaduto. Controlla rete o proxy del computer e riprova.",
527
+ cpolarDownloadHttp: "Il download ufficiale di cpolar ha restituito HTTP {status}. Riprova più tardi o controlla la pagina ufficiale.",
528
+ cpolarIntegrityFailed: "Il pacchetto cpolar non corrisponde alla versione verificata e non è stato installato. Riprova più tardi o aggiorna DSH Mobile; non aggirare la verifica.",
529
+ cpolarExtractFailed: "Download di cpolar completato, ma estrazione non riuscita. Controlla gli strumenti di estrazione e il software di sicurezza, poi riprova.",
530
+ cpolarStorageFailed: "Impossibile scrivere cpolar nella directory privata di DSH Mobile. Controlla spazio, permessi e software di sicurezza, poi riprova."
531
+ });
532
+ Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
533
+ cpolarDownloadFailed: "无法从 cpolar 官网下载组件。请检查这台电脑的网络或代理,然后重试。",
534
+ cpolarDownloadTimeout: "下载 cpolar 组件超时。请检查这台电脑的网络或代理,然后重试。",
535
+ cpolarDownloadHttp: "cpolar 官网下载返回 HTTP {status}。请稍后重试,或检查官网下载页。",
536
+ cpolarIntegrityFailed: "下载的 cpolar 组件未通过固定版本校验,因此没有安装。请稍后重试或更新 DSH Mobile,不要跳过校验。",
537
+ cpolarExtractFailed: "cpolar 下载完成,但解包失败。请检查系统解压组件和安全软件,然后重试。",
538
+ cpolarStorageFailed: "无法把 cpolar 写入 DSH Mobile 私有目录。请检查磁盘空间、目录权限和安全软件,然后重试。"
539
+ });
516
540
  Object.assign(MOBILE_CONTROL_MESSAGES.en, {
517
541
  lanSetupTitle: "Finish local network setup",
518
542
  lanSetupIntro: "Choose the network shared by this computer and your phone. DSH Mobile creates private certificates only after you confirm.",
@@ -1402,6 +1426,7 @@ window.__ModuleLoader__.load({
1402
1426
  "remote-needs-login": ["Tailscale is waiting for login to finish.", "Return to Remote access and continue login."],
1403
1427
  "remote-connecting": ["The {provider} remote channel is still connecting.", "Wait briefly, then check again."],
1404
1428
  "remote-controller-error": ["The {provider} controller reported {controllerCode}.", "Return to Remote access and reconnect."],
1429
+ "competing-remote-channel": ["Another remote plugin is injecting its own request channel; the phone may show that plugin’s pairing screen.", "Turn off dsh-remote-web-ui remote access on the computer, refresh, and pair using the current DSH Mobile QR code. Do not mix pairing codes."],
1405
1430
  "phone-network-unknown": ["The computer cannot determine whether the router isolates the phone.", "Confirm the phone and computer use the same network, then disable guest-network or AP isolation."]
1406
1431
  },
1407
1432
  it: {
@@ -1426,6 +1451,7 @@ window.__ModuleLoader__.load({
1426
1451
  "remote-needs-login": ["Tailscale attende il completamento dell’accesso.", "Torna ad Accesso remoto e continua l’accesso."],
1427
1452
  "remote-connecting": ["Il canale remoto {provider} è ancora in connessione.", "Attendi qualche istante e ripeti il controllo."],
1428
1453
  "remote-controller-error": ["Il controller {provider} ha segnalato {controllerCode}.", "Torna ad Accesso remoto e riconnettiti."],
1454
+ "competing-remote-channel": ["Un altro plugin remoto sta inserendo il proprio canale di richieste; sul telefono potrebbe apparire la sua schermata di abbinamento.", "Disattiva l’accesso remoto di dsh-remote-web-ui sul computer, aggiorna la pagina e usa il codice QR corrente di DSH Mobile. Non mescolare i codici."],
1429
1455
  "phone-network-unknown": ["Il computer non può stabilire se il router isola il telefono.", "Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP."]
1430
1456
  },
1431
1457
  zh: {
@@ -1450,6 +1476,7 @@ window.__ModuleLoader__.load({
1450
1476
  "remote-needs-login": ["Tailscale 正在等待完成登录。", "返回远程访问并继续登录。"],
1451
1477
  "remote-connecting": ["{provider} 远程通道仍在连接。", "等待片刻后重新检查。"],
1452
1478
  "remote-controller-error": ["{provider} 控制器报告 {controllerCode}。", "返回远程访问并重新连接。"],
1479
+ "competing-remote-channel": ["检测到另一个远程插件注入自己的请求通道;手机可能显示它的配对页面。", "在电脑端关闭 dsh-remote-web-ui 的远程访问并刷新,再用 DSH Mobile 当前二维码配对;不要混用配对码。"],
1453
1480
  "phone-network-unknown": ["电脑无法判断路由器是否隔离了手机。", "确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。"]
1454
1481
  }
1455
1482
  };
@@ -2599,12 +2626,13 @@ window.__ModuleLoader__.load({
2599
2626
  [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }
2600
2627
  [data-dsh-mobile-center] > * { min-width:0 !important; }
2601
2628
  [data-dsh-mobile-header] { box-sizing:border-box !important; width:calc(100% - 16px) !important; margin:0 8px !important; min-width:0; padding-top:max(4px,env(safe-area-inset-top)) !important; padding-right:8px !important; padding-left:42px !important; }
2629
+ [data-dsh-mobile-header] :is([class*="_titleRow"],[class*="_headerLeading"]) { touch-action:pan-y; }
2602
2630
  [data-dsh-mobile-header] [class*="_titleRow"] { box-sizing:border-box !important; display:flex !important; align-items:center !important; min-width:0; min-height:32px !important; height:32px !important; gap:6px !important; padding:0 6px !important; }
2603
2631
  [data-dsh-mobile-header] [class*="_titleCluster"] { min-width:0; }
2604
2632
  [data-dsh-mobile-header] [class*="_crumbs"] { min-width:0; overflow:hidden; }
2605
2633
  [data-dsh-mobile-header] [class*="_crumb"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
2606
- [data-dsh-mobile-header] [class*="_headerActions"] { min-width:0; overflow-x:auto; scrollbar-width:none; }
2607
- [data-dsh-mobile-header] [class*="_headerActions"]::-webkit-scrollbar { display:none; }
2634
+ /* Nested header actions (including the Jobs menu) must paint outside the row. */
2635
+ [data-dsh-mobile-header] [class*="_headerActions"] { flex:none; min-width:max-content; overflow:visible; }
2608
2636
  [data-dsh-mobile-header] [class*="_headerUtilities"] { gap:2px !important; }
2609
2637
  [data-dsh-mobile-header] [class*="_sessionLogButton"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }
2610
2638
  [data-dsh-mobile-header] [class*="_sessionLogButton"] > * { display:none !important; }
@@ -2691,6 +2719,15 @@ window.__ModuleLoader__.load({
2691
2719
  [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"] [class*="_sep"] { margin:0 6px !important; }
2692
2720
  /* Composer dock stats strip (turns/steps/tokens) reads small on phones. */
2693
2721
  [data-dsh-mobile-center] [data-slot="conversation.composer.dock"] [class*="_root"] { font-size:10px !important; line-height:16px !important; }
2722
+ /* The context ring is a composer-metadata _root too, so the metadata rule
2723
+ above stretched it across the whole dock. It cannot shrink, which left the
2724
+ session statistics beside it a few pixels wide, every number clipped to an
2725
+ ellipsis. Put the numbers in a wrapping left column and the ring in a right
2726
+ column it cannot grow out of: a phone then shows every number in full, and
2727
+ a wide screen keeps the ring beside them on one line. */
2728
+ [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"]:has(> [class*="_trigger"][aria-haspopup="dialog"]) { width:auto !important; max-width:none !important; flex:0 0 auto !important; }
2729
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) { display:grid !important; grid-template-columns:minmax(0,1fr) max-content !important; align-items:center !important; column-gap:12px !important; }
2730
+ [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"][data-composer-stats] { box-sizing:border-box !important; width:auto !important; max-width:none !important; flex:0 1 auto !important; flex-wrap:wrap !important; justify-content:flex-start !important; row-gap:2px !important; }
2694
2731
  /* Message runtime details are inline on desktop. Give the clock/runtime
2695
2732
  label its own wrapping row on narrow screens so TTFT and throughput do
2696
2733
  not push the action buttons or clip at the viewport edge. */
@@ -2739,7 +2776,6 @@ window.__ModuleLoader__.load({
2739
2776
  @keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }
2740
2777
  @keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }
2741
2778
  @media (max-width:420px) {
2742
- [data-dsh-mobile-header] [class*="_headerActions"] { max-width:42vw; }
2743
2779
  [data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { align-self:stretch !important; width:100% !important; }
2744
2780
  [data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }
2745
2781
  }
@@ -2756,6 +2792,48 @@ window.__ModuleLoader__.load({
2756
2792
  function firstByClassSuffix(root, suffix) {
2757
2793
  return Array.from(root.querySelectorAll("[class]")).find((element) => classToken(element, suffix));
2758
2794
  }
2795
+ /**
2796
+ * Whether this composer is the landing hero rather than the composer of an open
2797
+ * conversation. DSH marks the hero composer's root with a `_hero` class and drops
2798
+ * it once a session owns the surface, which is the only difference available
2799
+ * without the layout's session id.
2800
+ */
2801
+ function composerIsLandingHero(composer, depth = 4) {
2802
+ let node = composer;
2803
+ for (let step = 0; step < depth && node !== null; step += 1) {
2804
+ if (classToken(node, "_hero")) return true;
2805
+ node = node.parentElement;
2806
+ }
2807
+ return false;
2808
+ }
2809
+ /** Resolve only identities published by the current conversation or selected session row. */
2810
+ function resolveComposerSessionOrigin(composer, selectedRow, selectedRowToken) {
2811
+ const conversationRoot = composer?.closest("[data-conversation-session]") ?? null;
2812
+ const conversationId = conversationRoot?.getAttribute("data-conversation-session");
2813
+ if (conversationRoot !== null && conversationId) return {
2814
+ sessionRoot: conversationRoot,
2815
+ sessionId: conversationId
2816
+ };
2817
+ const dedicatedRoot = composer?.closest("[data-dsh-mobile-session]") ?? null;
2818
+ const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
2819
+ if (dedicatedRoot !== null && dedicatedId) return {
2820
+ sessionRoot: dedicatedRoot,
2821
+ sessionId: dedicatedId
2822
+ };
2823
+ if (selectedRow === null) return {
2824
+ sessionRoot: null,
2825
+ sessionId: null
2826
+ };
2827
+ const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
2828
+ return {
2829
+ sessionRoot: selectedRow,
2830
+ sessionId: `${selectedRowToken(selectedRow)}:${identity}`
2831
+ };
2832
+ }
2833
+ /** An id-less non-hero composer still accepts App soft-keyboard line breaks, but not camera results. */
2834
+ function activeSessionForSoftEnter(composer, sessionId) {
2835
+ return sessionId !== null || composer !== null && composer.closest(".dshm-main") !== null && !composerIsLandingHero(composer);
2836
+ }
2759
2837
  /** Find the stock DSH application frame without mistaking a feature card for the shell. */
2760
2838
  function resolveNativeMobileFrame(root, dedicatedCenter) {
2761
2839
  if (dedicatedCenter !== void 0) return void 0;
@@ -2782,6 +2860,93 @@ window.__ModuleLoader__.load({
2782
2860
  return marked;
2783
2861
  }
2784
2862
  const AUTO_HISTORY_THRESHOLD_PX = 64;
2863
+ /** Travel, in CSS pixels, that separates a header strip pan from a tap on a chip. */
2864
+ const STRIP_DRAG_THRESHOLD_PX = 8;
2865
+ /** Measure flowing title controls without counting an open absolute-positioned menu. */
2866
+ function measureHeaderStripOverflow(row) {
2867
+ const left = row.getBoundingClientRect().left;
2868
+ const controls = Array.from(row.children);
2869
+ const actions = row.querySelector(":scope > [class*=\"_titleCluster\"] [class*=\"_headerActions\"]");
2870
+ if (actions !== null) controls.push(actions);
2871
+ const right = Math.max(left, ...controls.map((control) => control.getBoundingClientRect().right));
2872
+ return Math.max(0, right - left - row.clientWidth);
2873
+ }
2874
+ /** Touch-only pan state; browser pointer cancellation does not interrupt the touch stream. */
2875
+ function createHeaderStripPanController(options) {
2876
+ let offset = 0;
2877
+ let drag;
2878
+ let suppressUntil = 0;
2879
+ const clamp = (value) => Math.min(Math.max(0, options.range()), Math.max(0, value));
2880
+ const setOffset = (value) => {
2881
+ const next = clamp(value);
2882
+ if (next === offset) return;
2883
+ offset = next;
2884
+ options.render(offset);
2885
+ };
2886
+ const end = () => {
2887
+ if (drag === void 0) return;
2888
+ if (drag.claimed) suppressUntil = options.now() + 400;
2889
+ drag = void 0;
2890
+ };
2891
+ const cancel = () => {
2892
+ if (drag?.claimed) suppressUntil = options.now() + 400;
2893
+ drag = void 0;
2894
+ };
2895
+ return {
2896
+ offset: () => offset,
2897
+ start: (x, y) => {
2898
+ drag = {
2899
+ x,
2900
+ y,
2901
+ offset,
2902
+ claimed: false
2903
+ };
2904
+ suppressUntil = 0;
2905
+ },
2906
+ move: (x, y) => {
2907
+ if (drag === void 0) return false;
2908
+ const travel = x - drag.x;
2909
+ if (!drag.claimed) {
2910
+ const drift = y - drag.y;
2911
+ if (Math.abs(travel) < STRIP_DRAG_THRESHOLD_PX && Math.abs(drift) < STRIP_DRAG_THRESHOLD_PX) return false;
2912
+ if (Math.abs(drift) > Math.abs(travel)) {
2913
+ drag = void 0;
2914
+ return false;
2915
+ }
2916
+ drag.claimed = true;
2917
+ }
2918
+ setOffset(drag.offset - travel);
2919
+ return true;
2920
+ },
2921
+ end,
2922
+ cancel,
2923
+ sync: () => {
2924
+ if (options.range() === 0) {
2925
+ drag = void 0;
2926
+ suppressUntil = 0;
2927
+ }
2928
+ setOffset(offset);
2929
+ },
2930
+ reveal: (left, right, visibleLeft, visibleRight) => {
2931
+ if (left < visibleLeft) setOffset(offset - (visibleLeft - left));
2932
+ else if (right > visibleRight) setOffset(offset + right - visibleRight);
2933
+ },
2934
+ resetClickSuppression: () => {
2935
+ suppressUntil = 0;
2936
+ },
2937
+ suppressClick: (inHeader, detail) => {
2938
+ if (suppressUntil === 0 || detail === 0) return false;
2939
+ const suppress = inHeader && options.now() <= suppressUntil;
2940
+ suppressUntil = 0;
2941
+ return suppress;
2942
+ },
2943
+ dispose: () => {
2944
+ drag = void 0;
2945
+ suppressUntil = 0;
2946
+ setOffset(0);
2947
+ }
2948
+ };
2949
+ }
2785
2950
  function controlledFileDragEvent(type, files, initialDropEffect) {
2786
2951
  const event = new Event(type, {
2787
2952
  bubbles: true,
@@ -2839,6 +3004,38 @@ window.__ModuleLoader__.load({
2839
3004
  function drawerScrimVisible(sidebarCollapsed, overlayActive) {
2840
3005
  return !sidebarCollapsed && overlayActive;
2841
3006
  }
3007
+ /** Let the stock page close one visible layer before Android navigates its WebView. */
3008
+ function installStockMobileBack(view, current, actions) {
3009
+ const onBack = (event) => {
3010
+ if (!event.cancelable || event.defaultPrevented) return;
3011
+ const layers = current();
3012
+ if (layers === null) return;
3013
+ if (layers.detailsOpen) {
3014
+ event.preventDefault();
3015
+ actions.closeDetails();
3016
+ } else if (layers.drawerOpen) {
3017
+ event.preventDefault();
3018
+ actions.closeDrawer();
3019
+ } else if (layers.mainPanelOpen) {
3020
+ event.preventDefault();
3021
+ actions.closeMainPanel();
3022
+ }
3023
+ };
3024
+ view.addEventListener("dsh-mobile:native-back", onBack);
3025
+ return () => {
3026
+ view.removeEventListener("dsh-mobile:native-back", onBack);
3027
+ };
3028
+ }
3029
+ function stockLayoutBackControl(value) {
3030
+ return typeof value === "object" && value !== null ? value : void 0;
3031
+ }
3032
+ /** Whether the stock layout can return from a selected main panel. */
3033
+ function stockMainPanelOpen(value) {
3034
+ const layout = stockLayoutBackControl(value);
3035
+ if (typeof layout?.selectPanel !== "function" || typeof layout.panelInfo?.getSnapshot !== "function") return false;
3036
+ const snapshot = layout.panelInfo.getSnapshot();
3037
+ return typeof snapshot === "object" && snapshot !== null && typeof snapshot.activePanelId === "string";
3038
+ }
2842
3039
  /** Only a known on-screen keyboard in an active App composer may change Enter into a line break. */
2843
3040
  function isSoftKeyboardEnterLineBreak(event, context) {
2844
3041
  if (context.nativeState?.imeVisible !== true || context.nativeState.noHardwareKeyboard !== true || !context.editable || !context.activeSession || context.commandMenuOpen || context.recentlyComposing) return false;
@@ -2886,7 +3083,7 @@ window.__ModuleLoader__.load({
2886
3083
  return !current.disposed && origin.generation === current.generation && origin.href === current.href && origin.composer !== null && current.composerConnected && origin.composer === current.composer && origin.sessionRoot !== null && origin.sessionRoot === current.sessionRoot && origin.sessionId !== null && origin.sessionId === current.sessionId;
2887
3084
  }
2888
3085
  /** Add mobile semantics without replacing feature trees. */
2889
- function installNativeMobileSurface() {
3086
+ function installNativeMobileSurface(backServices) {
2890
3087
  document.documentElement.classList.add("dsh-native-mobile-active");
2891
3088
  const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language];
2892
3089
  const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages);
@@ -2983,27 +3180,15 @@ window.__ModuleLoader__.load({
2983
3180
  const sessionTokens = /* @__PURE__ */ new WeakMap();
2984
3181
  let nextSessionToken = 0;
2985
3182
  const currentSessionOrigin = () => {
2986
- const dedicatedRoot = boundComposer?.closest("[data-dsh-mobile-session]") ?? null;
2987
- const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
2988
- if (dedicatedRoot !== null && typeof dedicatedId === "string" && dedicatedId !== "") return {
2989
- sessionRoot: dedicatedRoot,
2990
- sessionId: dedicatedId
2991
- };
2992
3183
  const selectedRow = document.querySelector("[role=\"treeitem\"][aria-selected=\"true\"]");
2993
- if (selectedRow === null) return {
2994
- sessionRoot: null,
2995
- sessionId: null
2996
- };
2997
- let token = sessionTokens.get(selectedRow);
2998
- if (token === void 0) {
2999
- token = `stock-${String(++nextSessionToken)}`;
3000
- sessionTokens.set(selectedRow, token);
3001
- }
3002
- const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
3003
- return {
3004
- sessionRoot: selectedRow,
3005
- sessionId: `${token}:${identity}`
3006
- };
3184
+ return resolveComposerSessionOrigin(boundComposer, selectedRow, (row) => {
3185
+ let token = sessionTokens.get(row);
3186
+ if (token === void 0) {
3187
+ token = `stock-${String(++nextSessionToken)}`;
3188
+ sessionTokens.set(row, token);
3189
+ }
3190
+ return token;
3191
+ });
3007
3192
  };
3008
3193
  const mediaRequestContext = () => {
3009
3194
  const session = currentSessionOrigin();
@@ -3180,6 +3365,101 @@ window.__ModuleLoader__.load({
3180
3365
  if (branch === null || branch.hasAttribute("disabled") || branch.getAttribute("aria-disabled") === "true") return;
3181
3366
  window.setTimeout(showBranchToast, 80);
3182
3367
  };
3368
+ let stripHeader;
3369
+ const stripParts = () => {
3370
+ const row = stripHeader?.querySelector("[class*=\"_titleRow\"]");
3371
+ if (row === void 0 || row === null) return void 0;
3372
+ return {
3373
+ row,
3374
+ lead: stripHeader?.querySelector("[class*=\"_headerLeading\"]") ?? void 0
3375
+ };
3376
+ };
3377
+ const stripRange = () => {
3378
+ const parts = stripParts();
3379
+ if (!overlayQuery.matches || parts === void 0) return 0;
3380
+ const overflow = measureHeaderStripOverflow(parts.row);
3381
+ return overflow > 1 ? overflow : 0;
3382
+ };
3383
+ const applyStrip = (offset) => {
3384
+ const parts = stripParts();
3385
+ if (parts === void 0) return;
3386
+ const value = offset === 0 ? "" : `translateX(${-offset}px)`;
3387
+ if (parts.row.style.transform !== value) parts.row.style.transform = value;
3388
+ if (parts.lead !== void 0 && parts.lead.style.transform !== value) parts.lead.style.transform = value;
3389
+ };
3390
+ const pan = createHeaderStripPanController({
3391
+ range: stripRange,
3392
+ render: applyStrip,
3393
+ now: () => performance.now()
3394
+ });
3395
+ const syncPanHeader = (header) => {
3396
+ if (header !== stripHeader) {
3397
+ pan.dispose();
3398
+ stripHeader = header;
3399
+ }
3400
+ pan.sync();
3401
+ applyStrip(pan.offset());
3402
+ };
3403
+ let activeTouchId;
3404
+ const onStripTouchStart = (event) => {
3405
+ if (activeTouchId !== void 0) pan.cancel();
3406
+ activeTouchId = void 0;
3407
+ pan.resetClickSuppression();
3408
+ if (event.touches.length !== 1) return;
3409
+ if (!overlayQuery.matches) return;
3410
+ if (!(event.target instanceof Element)) return;
3411
+ if (event.target.closest("[data-dsh-mobile-header]") !== stripHeader) return;
3412
+ if (event.target.closest("[data-dsh-mobile-header] [class*=\"_titleRow\"],[data-dsh-mobile-header] [class*=\"_headerLeading\"]") === null) return;
3413
+ if (stripRange() === 0) return;
3414
+ const touch = event.touches[0];
3415
+ if (touch === void 0) return;
3416
+ activeTouchId = touch.identifier;
3417
+ pan.start(touch.clientX, touch.clientY);
3418
+ };
3419
+ const onStripTouchMove = (event) => {
3420
+ if (activeTouchId === void 0) return;
3421
+ if (event.touches.length !== 1) {
3422
+ activeTouchId = void 0;
3423
+ pan.cancel();
3424
+ return;
3425
+ }
3426
+ const touch = event.touches[0];
3427
+ if (touch === void 0 || touch.identifier !== activeTouchId) return;
3428
+ if (pan.move(touch.clientX, touch.clientY)) event.preventDefault();
3429
+ };
3430
+ const onStripTouchEnd = (event) => {
3431
+ if (activeTouchId === void 0 || !Array.from(event.changedTouches).some((touch) => touch.identifier === activeTouchId)) return;
3432
+ activeTouchId = void 0;
3433
+ pan.end();
3434
+ };
3435
+ const onStripTouchCancel = (event) => {
3436
+ if (activeTouchId === void 0 || !Array.from(event.changedTouches).some((touch) => touch.identifier === activeTouchId)) return;
3437
+ activeTouchId = void 0;
3438
+ pan.cancel();
3439
+ };
3440
+ const onStripFocus = (event) => {
3441
+ if (!(event.target instanceof HTMLElement)) return;
3442
+ const header = event.target.closest("[data-dsh-mobile-header]");
3443
+ if (header === null || header !== stripHeader || stripRange() === 0 || event.target.closest("[class*=\"_titleRow\"],[class*=\"_headerLeading\"]") === null) return;
3444
+ const focused = event.target.getBoundingClientRect();
3445
+ const visible = header.getBoundingClientRect();
3446
+ pan.reveal(focused.left, focused.right, visible.left + 42, visible.right - 8);
3447
+ };
3448
+ const onStripClickCapture = (event) => {
3449
+ const inHeader = event.target instanceof Element && event.target.closest("[data-dsh-mobile-header]") !== null;
3450
+ if (!pan.suppressClick(inHeader, event.detail)) return;
3451
+ event.preventDefault();
3452
+ event.stopPropagation();
3453
+ };
3454
+ document.addEventListener("touchstart", onStripTouchStart, true);
3455
+ document.addEventListener("touchmove", onStripTouchMove, {
3456
+ capture: true,
3457
+ passive: false
3458
+ });
3459
+ document.addEventListener("touchend", onStripTouchEnd, true);
3460
+ document.addEventListener("touchcancel", onStripTouchCancel, true);
3461
+ document.addEventListener("focusin", onStripFocus, true);
3462
+ document.addEventListener("click", onStripClickCapture, true);
3183
3463
  document.addEventListener("click", onBranchClick, true);
3184
3464
  let frame;
3185
3465
  let sidebar;
@@ -3254,7 +3534,7 @@ window.__ModuleLoader__.load({
3254
3534
  disposeEnterBinding = editor === null || composerCard === null ? void 0 : bindComposerSoftEnter(editor, () => ({
3255
3535
  nativeState: window.__DSH_MOBILE_NATIVE__ === void 0 ? null : window.__DSH_MOBILE_KEYBOARD_STATE__,
3256
3536
  editable: editor.isConnected && editor.getAttribute("contenteditable") === "true" && editor.getAttribute("aria-disabled") !== "true" && editor.getAttribute("aria-haspopup") !== "menu" && composerCard.getAttribute("aria-busy") !== "true",
3257
- activeSession: currentSessionOrigin().sessionId !== null,
3537
+ activeSession: activeSessionForSoftEnter(composerCard, currentSessionOrigin().sessionId),
3258
3538
  commandMenuOpen: composerCard.querySelector("[data-trigger-menu],button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]") !== null
3259
3539
  }));
3260
3540
  };
@@ -3280,6 +3560,7 @@ window.__ModuleLoader__.load({
3280
3560
  const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
3281
3561
  markNativeMobileSettings(document);
3282
3562
  if (center === void 0) {
3563
+ syncPanHeader(void 0);
3283
3564
  syncComposerEnterNewline(null);
3284
3565
  bindHistoryScroller(void 0);
3285
3566
  syncMediaBinding(null);
@@ -3289,7 +3570,9 @@ window.__ModuleLoader__.load({
3289
3570
  }
3290
3571
  if (center !== void 0) {
3291
3572
  center.dataset.dshMobileCenter = "true";
3292
- center.querySelector("header")?.setAttribute("data-dsh-mobile-header", "true");
3573
+ const header = center.querySelector("header") ?? void 0;
3574
+ header?.setAttribute("data-dsh-mobile-header", "true");
3575
+ syncPanHeader(header);
3293
3576
  viewArea = firstByClassSuffix(center, "_viewArea");
3294
3577
  if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
3295
3578
  const conversation = center.querySelector("[data-conversation-scroll]");
@@ -3400,6 +3683,33 @@ window.__ModuleLoader__.load({
3400
3683
  if (sidebar?.dataset.open === "true") toggle?.click();
3401
3684
  });
3402
3685
  sync();
3686
+ const stopStockBack = installStockMobileBack(window, () => {
3687
+ if (frame?.isConnected !== true) return null;
3688
+ const lastColumn = frame.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
3689
+ return {
3690
+ detailsOpen: overlayQuery.matches && lastColumn !== void 0 && lastColumn !== "" && lastColumn !== "0px" && lastColumn !== "0",
3691
+ drawerOpen: overlayQuery.matches && sidebarRoot?.isConnected === true && toggle?.isConnected === true && !classToken(sidebarRoot, "_collapsed"),
3692
+ mainPanelOpen: stockMainPanelOpen(backServices.getLayout())
3693
+ };
3694
+ }, {
3695
+ closeDetails: () => {
3696
+ const layout = stockLayoutBackControl(backServices.getLayout());
3697
+ if (typeof layout?.closeRightbar !== "function") return;
3698
+ const sidebarRight = backServices.getSidebarRight();
3699
+ if (typeof sidebarRight === "object" && sidebarRight !== null) {
3700
+ const control = sidebarRight;
3701
+ if (typeof control.isExpanded === "function" && typeof control.toggleExpanded === "function" && control.isExpanded()) control.toggleExpanded();
3702
+ }
3703
+ layout.closeRightbar();
3704
+ },
3705
+ closeDrawer: () => {
3706
+ toggle?.click();
3707
+ },
3708
+ closeMainPanel: () => {
3709
+ const layout = stockLayoutBackControl(backServices.getLayout());
3710
+ if (typeof layout?.selectPanel === "function") layout.selectPanel(null);
3711
+ }
3712
+ });
3403
3713
  const disposeTaskWatcher = installTaskCompletionWatcher({
3404
3714
  completionFallback: false,
3405
3715
  label: (kind, sessionLabel) => kind === "done" ? {
@@ -3412,6 +3722,7 @@ window.__ModuleLoader__.load({
3412
3722
  });
3413
3723
  return () => {
3414
3724
  disposed = true;
3725
+ stopStockBack();
3415
3726
  disposeTaskWatcher();
3416
3727
  mediaRequestGeneration += 1;
3417
3728
  mediaPickerAbortController.abort();
@@ -3421,6 +3732,14 @@ window.__ModuleLoader__.load({
3421
3732
  observer.disconnect();
3422
3733
  overlayQuery.removeEventListener("change", schedule);
3423
3734
  document.removeEventListener("click", onBranchClick, true);
3735
+ document.removeEventListener("touchstart", onStripTouchStart, true);
3736
+ document.removeEventListener("touchmove", onStripTouchMove, true);
3737
+ document.removeEventListener("touchend", onStripTouchEnd, true);
3738
+ document.removeEventListener("touchcancel", onStripTouchCancel, true);
3739
+ document.removeEventListener("focusin", onStripFocus, true);
3740
+ document.removeEventListener("click", onStripClickCapture, true);
3741
+ pan.dispose();
3742
+ applyStrip(0);
3424
3743
  cameraButton.removeEventListener("pointerdown", quietMediaPointer);
3425
3744
  cameraButton.removeEventListener("click", takePhoto);
3426
3745
  if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer);
@@ -3445,6 +3764,16 @@ window.__ModuleLoader__.load({
3445
3764
  //#endregion
3446
3765
  //#region src/local-admin-host.ts
3447
3766
  /** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */
3767
+ const DESKTOP_ADMIN_HEADER = "x-dsh-mobile-desktop-admin";
3768
+ const DESKTOP_ADMIN_MARKER = "dsh-app://app";
3769
+ /** Add the forwarded Desktop marker only to this plugin's mutating admin requests. */
3770
+ function localAdminRequestHeaders(init, location) {
3771
+ const headers = new Headers(init?.headers);
3772
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
3773
+ headers.delete(DESKTOP_ADMIN_HEADER);
3774
+ if (init?.method?.toUpperCase() === "POST" && location.protocol === "dsh-app:" && location.hostname === "app") headers.set(DESKTOP_ADMIN_HEADER, DESKTOP_ADMIN_MARKER);
3775
+ return headers;
3776
+ }
3448
3777
  function unwrapBrackets(hostname) {
3449
3778
  return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
3450
3779
  }
@@ -3485,11 +3814,12 @@ window.__ModuleLoader__.load({
3485
3814
  /**
3486
3815
  * Whether the current browser document should mount the desktop Mobile access
3487
3816
  * control. Dedicated Mobile HTTPS (the phone surface) stays native even when
3488
- * the Host is a private LAN address.
3817
+ * the Host is a private LAN address. DSH Desktop (dsh-app://app) has neither,
3818
+ * so its protocol gated on the fixed hostname "app" counts too.
3489
3819
  */
3490
- function isDesktopAdminSurface(hostname, search = "", frontend) {
3820
+ function isDesktopAdminSurface(hostname, search = "", frontend, protocol = "") {
3491
3821
  const query = search.startsWith("?") ? search.slice(1) : search;
3492
- return isLocalAdminHostname(hostname) && frontend !== "dedicated" && !new URLSearchParams(query).has("dsh-mobile-preview");
3822
+ return (protocol === "dsh-app:" ? hostname === "app" : (protocol === "" || protocol === "http:" || protocol === "https:") && isLocalAdminHostname(hostname)) && frontend !== "dedicated" && !new URLSearchParams(query).has("dsh-mobile-preview");
3493
3823
  }
3494
3824
  //#endregion
3495
3825
  //#region src/client.ts
@@ -3497,6 +3827,64 @@ window.__ModuleLoader__.load({
3497
3827
  function parseMobileRemoteProvider(value) {
3498
3828
  return value === "cpolar" || value === "cloudflared" || value === "frp" || value === "origin" ? value : "tailscale";
3499
3829
  }
3830
+ /**
3831
+ * Set a surface's visibility; opening a page or overlay brings it to the front.
3832
+ * @param container - the registered surface element.
3833
+ * @param open - whether to show it.
3834
+ */
3835
+ function setMobileSurfaceOpen(container, open) {
3836
+ container.hidden = !open;
3837
+ if (open && container.isConnected && (container.dataset.dshMobileSurfacePlacement === "page" || container.dataset.dshMobileSurfacePlacement === "overlay")) container.parentElement?.append(container);
3838
+ }
3839
+ function visibleBackLayer(view, element) {
3840
+ if (!element.isConnected || element.hidden || element.closest("[hidden],[inert],[aria-hidden=\"true\"]") !== null) return false;
3841
+ const style = view.getComputedStyle(element);
3842
+ return style.display !== "none" && style.visibility !== "hidden" && element.getClientRects().length > 0;
3843
+ }
3844
+ /**
3845
+ * Consume page-level surfaces and dismissible DSH overlays before layout navigation.
3846
+ * @param view - the page window receiving the native Back request.
3847
+ * @param page - the current DSH document.
3848
+ * @param blocked - feedback for a foreground layer without a safe close action.
3849
+ * @returns a disposer for the capture listener.
3850
+ */
3851
+ function installMobileNativeBack(view, page, blocked) {
3852
+ const onBack = (event) => {
3853
+ if (!event.cancelable || event.defaultPrevented) return;
3854
+ const claim = () => {
3855
+ event.preventDefault();
3856
+ event.stopImmediatePropagation();
3857
+ };
3858
+ const surface = [...page.querySelectorAll("[data-dsh-mobile-surface-placement=\"page\"], [data-dsh-mobile-surface-placement=\"overlay\"]")].filter((element) => visibleBackLayer(view, element)).at(-1);
3859
+ if (surface !== void 0) {
3860
+ setMobileSurfaceOpen(surface, false);
3861
+ claim();
3862
+ return;
3863
+ }
3864
+ const layers = [...page.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]")].filter((element) => visibleBackLayer(view, element));
3865
+ const top = layers.at(-1);
3866
+ if (top === void 0) return;
3867
+ claim();
3868
+ if (top.getAttribute("role") === "dialog") {
3869
+ const wrapper = top.parentElement;
3870
+ const mask = wrapper?.firstElementChild;
3871
+ const maskStyle = mask === void 0 || mask === null ? void 0 : view.getComputedStyle(mask);
3872
+ if (wrapper?.getAttribute("role") === "presentation" && wrapper.children.length === 2 && wrapper.lastElementChild === top && mask?.tagName === "DIV" && mask.getAttribute("aria-hidden") === "true" && mask.isConnected && !mask.hidden && maskStyle?.display !== "none" && maskStyle?.visibility !== "hidden" && mask.getClientRects().length > 0) {
3873
+ mask.click();
3874
+ return;
3875
+ }
3876
+ } else if (top.getAttribute("data-menu-material") === "translucent" && layers.filter((layer) => layer.getAttribute("role") === "menu").length === 1) {
3877
+ page.dispatchEvent(new Event("pointerdown", { bubbles: true }));
3878
+ return;
3879
+ }
3880
+ blocked();
3881
+ };
3882
+ const capture = { capture: true };
3883
+ view.addEventListener("dsh-mobile:native-back", onBack, capture);
3884
+ return () => {
3885
+ view.removeEventListener("dsh-mobile:native-back", onBack, capture);
3886
+ };
3887
+ }
3500
3888
  const queuedDefinitions = [];
3501
3889
  let queuedLegacyMount;
3502
3890
  if (typeof window !== "undefined" && window.dshMobile === void 0) window.dshMobile = {
@@ -3517,6 +3905,18 @@ window.__ModuleLoader__.load({
3517
3905
  function selectedMobileControlLocale() {
3518
3906
  return selectMobileControlLocale(document.documentElement.lang, navigator.languages?.length ? navigator.languages : [navigator.language]);
3519
3907
  }
3908
+ /**
3909
+ * Guidance when an open third-party layer has no safe close action.
3910
+ * @param locale - current Mobile UI language.
3911
+ * @returns localized guidance.
3912
+ */
3913
+ function mobileBackBlockedMessage(locale) {
3914
+ return {
3915
+ en: "Close the open panel first.",
3916
+ it: "Chiudi prima il pannello aperto.",
3917
+ zh: "请先关闭当前弹层。"
3918
+ }[locale];
3919
+ }
3520
3920
  /** Remount one plugin-owned surface when DSH changes the document language. */
3521
3921
  function installDshLanguageBoundSurface(install) {
3522
3922
  let locale = selectedMobileControlLocale();
@@ -3810,10 +4210,7 @@ window.__ModuleLoader__.load({
3810
4210
  const response = await fetch(path, {
3811
4211
  ...init,
3812
4212
  signal: controller.signal,
3813
- headers: {
3814
- "content-type": "application/json",
3815
- ...init?.headers
3816
- }
4213
+ headers: localAdminRequestHeaders(init, location)
3817
4214
  });
3818
4215
  const body = await response.json();
3819
4216
  if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : `HTTP ${String(response.status)}`);
@@ -3913,6 +4310,18 @@ window.__ModuleLoader__.load({
3913
4310
  control_channel_failed: "controlChannelFailed",
3914
4311
  cpolar_component_missing: "cpolarMissing",
3915
4312
  cpolar_component_invalid: "cpolarInvalid",
4313
+ cpolar_download_failed: "cpolarDownloadFailed",
4314
+ cpolar_download_timeout: "cpolarDownloadTimeout",
4315
+ cpolar_download_size_mismatch: "cpolarIntegrityFailed",
4316
+ cpolar_download_hash_mismatch: "cpolarIntegrityFailed",
4317
+ cpolar_executable_hash_mismatch: "cpolarIntegrityFailed",
4318
+ cpolar_download_redirect_missing: "cpolarDownloadFailed",
4319
+ cpolar_download_redirect_invalid: "cpolarDownloadFailed",
4320
+ cpolar_download_redirect_rejected: "cpolarDownloadFailed",
4321
+ cpolar_installer_missing: "cpolarExtractFailed",
4322
+ cpolar_executable_missing: "cpolarExtractFailed",
4323
+ cpolar_extract_failed: "cpolarExtractFailed",
4324
+ cpolar_storage_failed: "cpolarStorageFailed",
3916
4325
  cpolar_config_missing: "cpolarConfigMissing",
3917
4326
  cpolar_config_invalid: "cpolarConfigInvalid",
3918
4327
  cpolar_port_unavailable: "cpolarPortUnavailable",
@@ -4048,6 +4457,8 @@ window.__ModuleLoader__.load({
4048
4457
  */
4049
4458
  const remoteFailureTextFor = (error, fallbackKey) => {
4050
4459
  const message = error instanceof Error ? error.message : String(error);
4460
+ const cpolarHttpStatus = /^cpolar_download_http_(\d{3})$/u.exec(message)?.[1];
4461
+ if (cpolarHttpStatus !== void 0) return t("cpolarDownloadHttp", { status: cpolarHttpStatus });
4051
4462
  const key = REMOTE_ERROR_MESSAGE_KEYS[message];
4052
4463
  return key === void 0 ? t(fallbackKey, { error: message }) : t(key);
4053
4464
  };
@@ -7629,6 +8040,9 @@ window.__ModuleLoader__.load({
7629
8040
  shellLayer().append(node);
7630
8041
  window.setTimeout(() => node.remove(), 2600);
7631
8042
  };
8043
+ const stopNativeBack = installMobileNativeBack(window, document, () => {
8044
+ toast(mobileBackBlockedMessage(selectedMobileControlLocale()));
8045
+ });
7632
8046
  const materializeNativeFile = (value) => {
7633
8047
  if (typeof value !== "object" || value === null || !("base64" in value) || !("name" in value)) return value;
7634
8048
  const candidate = value;
@@ -7761,6 +8175,7 @@ window.__ModuleLoader__.load({
7761
8175
  return registerUniqueDisposable(surfaces, surfaceIds, surface.id, () => {
7762
8176
  const container = element("section");
7763
8177
  container.dataset.dshMobileSurface = surface.id;
8178
+ container.dataset.dshMobileSurfacePlacement = surface.placement;
7764
8179
  container.hidden = surface.placement === "page" || surface.placement === "overlay";
7765
8180
  container.style.cssText = surface.placement === "page" || surface.placement === "overlay" ? "position:absolute;inset:0;overflow:auto;background:var(--dsw-alias-bg-layer-1,#fff);padding:16px;pointer-events:auto" : "pointer-events:auto";
7766
8181
  const host = () => surface.placement === "page" || surface.placement === "overlay" ? shellLayer() : surfaceHost(surface.placement) ?? shellLayer();
@@ -7813,12 +8228,12 @@ window.__ModuleLoader__.load({
7813
8228
  open: (surfaceId) => {
7814
8229
  ensureCurrent();
7815
8230
  const entry = surfaces.get(surfaceId);
7816
- if (entry !== void 0) entry.container.hidden = false;
8231
+ if (entry !== void 0) setMobileSurfaceOpen(entry.container, true);
7817
8232
  },
7818
8233
  close: (surfaceId) => {
7819
8234
  ensureCurrent();
7820
8235
  const entry = surfaces.get(surfaceId);
7821
- if (entry !== void 0) entry.container.hidden = true;
8236
+ if (entry !== void 0) setMobileSurfaceOpen(entry.container, false);
7822
8237
  },
7823
8238
  toast: (message) => {
7824
8239
  ensureCurrent();
@@ -8190,6 +8605,7 @@ window.__ModuleLoader__.load({
8190
8605
  });
8191
8606
  return () => {
8192
8607
  disposed = true;
8608
+ stopNativeBack();
8193
8609
  stopEvents();
8194
8610
  stopRefresh();
8195
8611
  started = false;
@@ -8286,7 +8702,7 @@ window.__ModuleLoader__.load({
8286
8702
  return trustAuthenticatedGatewayConnection(ctx.get("connection"));
8287
8703
  }, "dsh-mobile: authenticated gateway client trust");
8288
8704
  ctx.effect(() => {
8289
- const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__);
8705
+ const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__, location.protocol);
8290
8706
  const style = element("style");
8291
8707
  style.dataset.plugin = "dsh-mobile";
8292
8708
  style.textContent = desktopAdmin ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
@@ -8309,7 +8725,10 @@ window.__ModuleLoader__.load({
8309
8725
  };
8310
8726
  });
8311
8727
  const removeCustom = installCustomAssets();
8312
- const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface);
8728
+ const removeSurface = installDshLanguageBoundSurface(() => installNativeMobileSurface({
8729
+ getLayout: () => ctx.get("layout"),
8730
+ getSidebarRight: () => ctx.get("sidebarRight")
8731
+ }));
8313
8732
  return () => {
8314
8733
  removeSettingsAction();
8315
8734
  removeCustom();
@@ -8392,6 +8811,8 @@ window.__ModuleLoader__.load({
8392
8811
  exports.handleMissingExtensionManifest = handleMissingExtensionManifest;
8393
8812
  exports.inject = inject;
8394
8813
  exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
8814
+ exports.installMobileNativeBack = installMobileNativeBack;
8815
+ exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
8395
8816
  exports.normalizeDiagnosticOverall = normalizeDiagnosticOverall;
8396
8817
  exports.normalizeDiagnosticStatus = normalizeDiagnosticStatus;
8397
8818
  exports.parseMobileExtensionManifest = parseMobileExtensionManifest;
@@ -8402,6 +8823,7 @@ window.__ModuleLoader__.load({
8402
8823
  exports.renderDiagnosticPayloadSafely = renderDiagnosticPayloadSafely;
8403
8824
  exports.selectMobileControlLocale = selectMobileControlLocale;
8404
8825
  exports.selectedMobileControlLocale = selectedMobileControlLocale;
8826
+ exports.setMobileSurfaceOpen = setMobileSurfaceOpen;
8405
8827
  exports.startExtensionChangeStream = startExtensionChangeStream;
8406
8828
  exports.startLifecycleRefreshScheduler = startLifecycleRefreshScheduler;
8407
8829
  exports.trustAuthenticatedGatewayConnection = trustAuthenticatedGatewayConnection;