dsh-mobile 0.5.3 → 0.5.5

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
@@ -1212,7 +1212,7 @@ window.__ModuleLoader__.load({
1212
1212
  cloudflaredBadge: "Account optional",
1213
1213
  cloudflaredDescription: "Installs the official cloudflared client on demand. A quick tunnel needs no account and gives a temporary public address; a named tunnel uses a Cloudflare account token and keeps one hostname that survives restarts.",
1214
1214
  prepareCloudflared: "Prepare cloudflared",
1215
- cloudflaredUnsupported: "Windows x64 and Linux x64/arm64 are currently supported.",
1215
+ cloudflaredUnsupported: "Windows x64, Linux x64/arm64, and macOS x64/arm64 are supported.",
1216
1216
  cloudflaredNotInstalled: "Not installed. A fixed version is downloaded from the official cloudflared release page only after you click the button below.",
1217
1217
  cloudflaredReady: "Official component {version} is ready.",
1218
1218
  cloudflaredQuickNote: "Quick tunnel: no Cloudflare account is needed, but Cloudflare positions quick tunnels for testing — the address is random, rate-limited, and carries no uptime guarantee. Do not rely on one for access that must stay reachable.",
@@ -1280,7 +1280,7 @@ window.__ModuleLoader__.load({
1280
1280
  cloudflaredBadge: "Account facoltativo",
1281
1281
  cloudflaredDescription: "Installa su richiesta il client cloudflared ufficiale. Il tunnel rapido non richiede account e fornisce un indirizzo pubblico temporaneo; il tunnel con nome usa il token di un account Cloudflare e mantiene un hostname stabile tra i riavvii.",
1282
1282
  prepareCloudflared: "Prepara cloudflared",
1283
- cloudflaredUnsupported: "Attualmente sono supportati Windows x64 e Linux x64/arm64.",
1283
+ cloudflaredUnsupported: "Sono supportati Windows x64, Linux x64/arm64 e macOS x64/arm64.",
1284
1284
  cloudflaredNotInstalled: "Non installato. Una versione fissa viene scaricata dalla pagina ufficiale di cloudflared solo dopo aver premuto il pulsante.",
1285
1285
  cloudflaredReady: "Componente ufficiale {version} pronto.",
1286
1286
  cloudflaredQuickNote: "Tunnel rapido: non serve un account Cloudflare, ma Cloudflare li considera pensati per i test — l’indirizzo è casuale, soggetto a limiti di frequenza e senza garanzia di disponibilità. Non usarlo per un accesso che deve restare raggiungibile.",
@@ -1348,7 +1348,7 @@ window.__ModuleLoader__.load({
1348
1348
  cloudflaredBadge: "账号可选",
1349
1349
  cloudflaredDescription: "按需安装官方 cloudflared 客户端。快速隧道无需账号,提供临时公网地址;命名隧道使用 Cloudflare 账号令牌,地址固定、重启不变。",
1350
1350
  prepareCloudflared: "准备 cloudflared",
1351
- cloudflaredUnsupported: "当前支持 Windows x64 与 Linux x64/arm64。",
1351
+ cloudflaredUnsupported: "支持 Windows x64、Linux x64/arm64 与 macOS x64/arm64。",
1352
1352
  cloudflaredNotInstalled: "尚未安装。只有点击下方按钮后,才会从 cloudflared 官方发布页下载固定版本。",
1353
1353
  cloudflaredReady: "官方组件 {version} 已就绪。",
1354
1354
  cloudflaredQuickNote: "快速隧道:无需 Cloudflare 账号,但官方定位是测试用途 —— 地址随机、有限流、无可用性保证。请勿用于必须长期可达的访问。",
@@ -2631,6 +2631,14 @@ window.__ModuleLoader__.load({
2631
2631
  the query restores the fixed scrim, and its more specific [hidden] rule
2632
2632
  keeps winning there. */
2633
2633
  .dsh-native-mobile-backdrop,.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:none; }
2634
+ /* The content-font preference may exceed Safari's focused-editable minimum.
2635
+ The html-level class exists on every non-desktop mobile page, so it also
2636
+ covers body-level portals (model picker search) the center column never
2637
+ contained; the center marker stays alongside it because a disposed
2638
+ surface removes the html class while styles applied from its string can
2639
+ outlive the class on a page that still carries the center column. */
2640
+ html.dsh-native-mobile-active :is(input,textarea,[contenteditable="true"],[contenteditable="plaintext-only"],[contenteditable=""]),
2641
+ [data-dsh-mobile-center] :is(input,textarea,[contenteditable="true"],[contenteditable="plaintext-only"],[contenteditable=""]) { font-size:max(16px,1em,var(--dsh-content-font-size,1em)) !important; }
2634
2642
  .dsh-mobile-settings_row { display:flex; align-items:center; gap:8px; padding:16px 0; border-bottom:0.5px solid var(--dsw-alias-border-l2); }
2635
2643
  .dsh-mobile-settings_rowText { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; padding-right:48px; }
2636
2644
  .dsh-mobile-settings_title { color:var(--dsw-alias-label-primary); font-size:14px; font-weight:400; line-height:22px; }
@@ -2723,7 +2731,6 @@ window.__ModuleLoader__.load({
2723
2731
  [data-dsh-mobile-settings-options] [class*="_rowActions"] button { flex:none !important; width:auto !important; min-width:44px !important; max-width:none !important; min-height:44px !important; padding-inline:10px !important; white-space:nowrap !important; word-break:keep-all !important; writing-mode:horizontal-tb !important; }
2724
2732
  [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition="true"],
2725
2733
  [data-dsh-mobile-view][data-dsh-mobile-view-transition="true"] { animation:dsh-mobile-view-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }
2726
- [data-dsh-mobile-center] textarea { font-size:16px !important; }
2727
2734
  /* Markdown tables use content-sized columns. Small tables fill the phone;
2728
2735
  wider tables keep readable cells and scroll inside their own region. */
2729
2736
  [data-dsh-mobile-table-scroll] { box-sizing:border-box; width:100%; max-width:100%; overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch; }
@@ -2750,12 +2757,15 @@ window.__ModuleLoader__.load({
2750
2757
  [data-dsh-mobile-message-column] [class*="_body"] { max-width:100% !important; overflow-wrap:anywhere; }
2751
2758
  /* Keep folded and expanded reasoning visually separate from the reply. */
2752
2759
  [data-dsh-mobile-message-column] [class*="_body"] > div:has(> [data-variant="think"]) { margin-bottom:12px !important; }
2753
- [data-dsh-mobile-center] [data-composer-card] ~ [class*="_root"],
2754
- [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"] { box-sizing:border-box !important; width:100% !important; max-width:100% !important; margin-bottom:-6px !important; padding:3px 4px 0 !important; font-size:11px !important; line-height:18px !important; white-space:normal !important; overflow:visible !important; text-overflow:clip !important; }
2760
+ [data-dsh-mobile-center] [data-composer-card] ~ [class*="_root"]:not(:where(input,textarea,[contenteditable])),
2761
+ [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"]:not(:where(input,textarea,[contenteditable])) { box-sizing:border-box !important; width:100% !important; max-width:100% !important; margin-bottom:-6px !important; padding:3px 4px 0 !important; font-size:11px !important; line-height:18px !important; white-space:normal !important; overflow:visible !important; text-overflow:clip !important; }
2755
2762
  [data-dsh-mobile-center] [data-composer-card] ~ [class*="_root"] [class*="_sep"],
2756
2763
  [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"] [class*="_sep"] { margin:0 6px !important; }
2757
- /* Composer dock stats strip (turns/steps/tokens) reads small on phones. */
2758
- [data-dsh-mobile-center] [data-slot="conversation.composer.dock"] [class*="_root"] { font-size:10px !important; line-height:16px !important; }
2764
+ /* Composer dock stats strip (turns/steps/tokens) reads small on phones.
2765
+ :not(:where(...)) keeps editables exempt at zero specificity cost, so
2766
+ the small !important fonts cannot beat the 16px editable floor while
2767
+ the dock spacing rules keep their cascade position. */
2768
+ [data-dsh-mobile-center] [data-slot="conversation.composer.dock"] [class*="_root"]:not(:where(input,textarea,[contenteditable])) { font-size:10px !important; line-height:16px !important; }
2759
2769
  /* The context ring is a composer-metadata _root too, so the metadata rule
2760
2770
  above stretched it across the whole dock. It cannot shrink, which left the
2761
2771
  session statistics beside it a few pixels wide, every number clipped to an
@@ -2812,7 +2822,13 @@ window.__ModuleLoader__.load({
2812
2822
  .dsh-mobile-media-action svg { flex:none; width:16px; height:16px; color:var(--dsw-alias-label-tertiary,currentColor); }
2813
2823
  .dsh-mobile-media-action span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
2814
2824
  [data-dsh-mobile-center] [class*="_composer"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }
2815
- /* The desktop composer intentionally wraps whole toolbar groups. On a phone,
2825
+ /* Collapse the scroll viewport only; the editor, draft and attachments stay
2826
+ owned by DSH. Toolbar focus keeps the card expanded during menu taps. */
2827
+ [data-dsh-mobile-center] [data-composer-card]:not(:focus-within) > [data-input-scroll] { max-height:72px !important; overflow-y:auto !important; }
2828
+ /* Stock Send/Queue/Steer and Stop keep their handlers and disabled state. */
2829
+ [data-dsh-mobile-composer-row] button { min-height:44px !important; touch-action:manipulation; }
2830
+ [data-dsh-mobile-composer-row] button[class*="_primary"] { min-width:44px !important; flex-shrink:0 !important; }
2831
+ /* The desktop composer intentionally wraps whole toolbar groups. On a phone,
2816
2832
  dynamic model and status labels made that row alternate between one and
2817
2833
  two lines. Keep two stable columns and let only the model label shrink. */
2818
2834
  [data-dsh-mobile-composer-row] { display:grid !important; grid-template-columns:max-content minmax(0,1fr) !important; align-items:center !important; gap:4px 8px !important; }
@@ -3996,6 +4012,7 @@ window.__ModuleLoader__.load({
3996
4012
  }
3997
4013
  /**
3998
4014
  * Consume page-level surfaces and dismissible DSH overlays before layout navigation.
4015
+ * Composer model popups use DSH's Escape handling to return from a pane before closing.
3999
4016
  * @param view - the page window receiving the native Back request.
4000
4017
  * @param page - the current DSH document.
4001
4018
  * @param blocked - feedback for a foreground layer without a safe close action.
@@ -4014,10 +4031,35 @@ window.__ModuleLoader__.load({
4014
4031
  claim();
4015
4032
  return;
4016
4033
  }
4017
- const layers = [...page.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]")].filter((element) => visibleBackLayer(view, element));
4034
+ const modelPopups = /* @__PURE__ */ new Map();
4035
+ for (const trigger of page.querySelectorAll("[data-dsh-mobile-composer-model-trigger][aria-expanded=\"true\"]")) {
4036
+ const popupId = trigger.getAttribute("aria-controls");
4037
+ const popup = popupId === null ? null : page.getElementById(popupId);
4038
+ if (popup !== null && visibleBackLayer(view, trigger) && visibleBackLayer(view, popup) && (popup.getAttribute("role") === "menu" || popup.getAttribute("role") === "group")) modelPopups.set(popup, trigger);
4039
+ }
4040
+ const layers = [...page.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"], [role=\"group\"][id]")].filter((element) => visibleBackLayer(view, element) && (element.getAttribute("role") !== "group" || modelPopups.has(element)));
4018
4041
  const top = layers.at(-1);
4019
4042
  if (top === void 0) return;
4020
4043
  claim();
4044
+ const modelEntry = [...modelPopups].find(([popup]) => popup.contains(top));
4045
+ if (modelEntry !== void 0) {
4046
+ const [modelPopup, trigger] = modelEntry;
4047
+ if (layers.some((layer) => !modelPopup.contains(layer))) {
4048
+ blocked();
4049
+ return;
4050
+ }
4051
+ const focused = page.activeElement;
4052
+ const target = focused !== null && modelPopup.contains(focused) ? focused : trigger;
4053
+ const escape = new KeyboardEvent("keydown", {
4054
+ key: "Escape",
4055
+ code: "Escape",
4056
+ bubbles: true,
4057
+ cancelable: true
4058
+ });
4059
+ target.dispatchEvent(escape);
4060
+ if (!escape.defaultPrevented) blocked();
4061
+ return;
4062
+ }
4021
4063
  if (top.getAttribute("role") === "dialog") {
4022
4064
  const wrapper = top.parentElement;
4023
4065
  const mask = wrapper?.firstElementChild;