dsh-mobile-access 1.1.0 → 1.1.2
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/README.md +17 -2
- package/client.js +108 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ iPhone 打开「设置 → 无线局域网」,确认和 Mac 连的是同一个
|
|
|
79
79
|
### 第 2 步:在 Mac 终端执行一条命令
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
dsh plugin --profile web add dsh-mobile-access@1.1.
|
|
82
|
+
dsh plugin --profile web add dsh-mobile-access@1.1.2 --registry=https://registry.npmjs.org/
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
这条命令做三件事:
|
|
@@ -175,6 +175,21 @@ Safari 分享按钮 → 添加到主屏幕
|
|
|
175
175
|
- 插件只在 iPhone / iPad 上安装「Return 换行」监听,Mac 和非 Apple 平台不会安装;
|
|
176
176
|
- 左边栏只多一个「手机访问」入口,其他入口和面板保持原样。
|
|
177
177
|
|
|
178
|
+
### iPhone 上切换大模型时页面放大、键盘自动弹出怎么办
|
|
179
|
+
|
|
180
|
+
从 `1.1.2` 起不需要改 DSH,也不需要关掉 Safari 的缩放:
|
|
181
|
+
|
|
182
|
+
- 插件只在 iPhone / iPad 上把可编辑控件(输入框、模型搜索框、提示词编辑器)
|
|
183
|
+
的字号固定到 16px,并给按钮加 `touch-action: manipulation`,避免 iOS
|
|
184
|
+
Safari 因为字号小于 16px 自动放大页面;
|
|
185
|
+
- 打开右下角模型选择时,插件不会让模型搜索框自动抢焦点,所以键盘不会自动
|
|
186
|
+
弹出;需要搜索时,点一下搜索框再输入即可;
|
|
187
|
+
- 如果提示词键盘本来就是打开的,点模型入口时会先收起键盘;
|
|
188
|
+
- Mac 和非 Apple 平台不安装这些处理,行为保持原样。
|
|
189
|
+
|
|
190
|
+
如果页面已经处于放大状态,先点 Safari 地址栏刷新一次,或双击页面空白处
|
|
191
|
+
复位,再重新打开模型选择即可。
|
|
192
|
+
|
|
178
193
|
### 配对码过期了怎么办
|
|
179
194
|
|
|
180
195
|
配对码默认 **10 分钟**有效。过期后:
|
|
@@ -336,7 +351,7 @@ iPhone 软键盘没有可用的 Shift 键,因此插件在 **iOS / iPadOS** 上
|
|
|
336
351
|
### 推荐:npm 官方命令
|
|
337
352
|
|
|
338
353
|
```bash
|
|
339
|
-
dsh plugin --profile web add dsh-mobile-access@1.1.
|
|
354
|
+
dsh plugin --profile web add dsh-mobile-access@1.1.2 --registry=https://registry.npmjs.org/
|
|
340
355
|
```
|
|
341
356
|
|
|
342
357
|
### 使用 `@latest`
|
package/client.js
CHANGED
|
@@ -83,7 +83,6 @@ function installIOSReturnNewline() {
|
|
|
83
83
|
Object.defineProperty(event, 'shiftKey', { configurable: true, value: true });
|
|
84
84
|
}
|
|
85
85
|
catch {
|
|
86
|
-
// Fall through to the explicit edit fallback.
|
|
87
86
|
}
|
|
88
87
|
const currentShift = event.shiftKey;
|
|
89
88
|
if (currentShift !== true) {
|
|
@@ -140,12 +139,107 @@ function installClipboardFallback() {
|
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
141
|
catch {
|
|
143
|
-
// A host page that froze the descriptor wins; never throw during cleanup.
|
|
144
142
|
}
|
|
145
143
|
};
|
|
146
144
|
}
|
|
147
145
|
return () => undefined;
|
|
148
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* iOS Safari auto-zooms a focused input, textarea, select or contenteditable
|
|
149
|
+
* whose computed font-size is below 16px. The official model picker's search
|
|
150
|
+
* field is 12px and the composer's editor is 14px, so tapping the model chip
|
|
151
|
+
* or its search box zooms the page and leaves the fixed-position model menu
|
|
152
|
+
* partly off-screen. Force only those editable controls to 16px on coarse
|
|
153
|
+
* pointers; Mac (fine pointer / hover) is untouched, and pinch zoom remains
|
|
154
|
+
* available because the viewport meta is not changed.
|
|
155
|
+
*/
|
|
156
|
+
const IOS_INPUT_ZOOM_GUARD_ID = 'dsh-ma-ios-input-zoom-guard';
|
|
157
|
+
const IOS_INPUT_ZOOM_GUARD_CSS = `@media (hover: none) and (pointer: coarse) {
|
|
158
|
+
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
|
|
159
|
+
textarea,
|
|
160
|
+
select,
|
|
161
|
+
[contenteditable="true"],
|
|
162
|
+
[data-composer-input],
|
|
163
|
+
[role="searchbox"],
|
|
164
|
+
[role="textbox"] {
|
|
165
|
+
font-size: 16px !important;
|
|
166
|
+
}
|
|
167
|
+
button,
|
|
168
|
+
[role="button"],
|
|
169
|
+
[role="menuitem"],
|
|
170
|
+
[role="menuitemradio"] {
|
|
171
|
+
touch-action: manipulation;
|
|
172
|
+
}
|
|
173
|
+
}`;
|
|
174
|
+
function installIOSInputZoomGuard() {
|
|
175
|
+
if (!isAppleMobile())
|
|
176
|
+
return () => undefined;
|
|
177
|
+
document.getElementById(IOS_INPUT_ZOOM_GUARD_ID)?.remove();
|
|
178
|
+
const style = document.createElement('style');
|
|
179
|
+
style.id = IOS_INPUT_ZOOM_GUARD_ID;
|
|
180
|
+
style.textContent = IOS_INPUT_ZOOM_GUARD_CSS;
|
|
181
|
+
document.head.appendChild(style);
|
|
182
|
+
return () => style.remove();
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The official model menu can move focus into its search field while it opens.
|
|
186
|
+
* On iPhone that opens the software keyboard and shifts the fixed-position menu
|
|
187
|
+
* above the visible viewport. Suppress only programmatic search-box focus until
|
|
188
|
+
* the user deliberately taps that field; blur a focused composer control before
|
|
189
|
+
* any composer menu opens. Native taps still focus normally, and Mac is untouched.
|
|
190
|
+
*/
|
|
191
|
+
function installIOSModelKeyboardGuard() {
|
|
192
|
+
if (!isAppleMobile())
|
|
193
|
+
return () => undefined;
|
|
194
|
+
if (typeof HTMLInputElement === 'undefined')
|
|
195
|
+
return () => undefined;
|
|
196
|
+
const doc = document;
|
|
197
|
+
const allowed = new WeakSet();
|
|
198
|
+
const onPointerDown = (event) => {
|
|
199
|
+
const target = event.target;
|
|
200
|
+
if (target === null || typeof target.closest !== 'function')
|
|
201
|
+
return;
|
|
202
|
+
const search = target.closest('input[role="searchbox"]');
|
|
203
|
+
if (search !== null) {
|
|
204
|
+
allowed.add(search);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const trigger = target.closest('button[aria-haspopup="menu"]');
|
|
208
|
+
if (trigger === null || trigger.closest('[data-composer-card]') === null)
|
|
209
|
+
return;
|
|
210
|
+
const active = doc.activeElement;
|
|
211
|
+
if (active !== null && active !== trigger && typeof active.blur === 'function') {
|
|
212
|
+
try {
|
|
213
|
+
active.blur();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const prototype = HTMLInputElement.prototype;
|
|
220
|
+
const originalFocus = prototype.focus;
|
|
221
|
+
const guardedFocus = function (options) {
|
|
222
|
+
if (this.getAttribute('role') === 'searchbox' && !allowed.has(this))
|
|
223
|
+
return;
|
|
224
|
+
originalFocus.call(this, options);
|
|
225
|
+
};
|
|
226
|
+
Object.defineProperty(prototype, 'focus', {
|
|
227
|
+
configurable: true,
|
|
228
|
+
writable: true,
|
|
229
|
+
value: guardedFocus,
|
|
230
|
+
});
|
|
231
|
+
doc.addEventListener('pointerdown', onPointerDown, true);
|
|
232
|
+
return () => {
|
|
233
|
+
doc.removeEventListener('pointerdown', onPointerDown, true);
|
|
234
|
+
if (prototype.focus === guardedFocus) {
|
|
235
|
+
Object.defineProperty(prototype, 'focus', {
|
|
236
|
+
configurable: true,
|
|
237
|
+
writable: true,
|
|
238
|
+
value: originalFocus,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
149
243
|
function useMobileAccessState() {
|
|
150
244
|
const [state, setState] = React.useState(null);
|
|
151
245
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -275,7 +369,6 @@ function statusOf(state, loading, error, t) {
|
|
|
275
369
|
return { label: t('statusExpired'), tone: 'warn' };
|
|
276
370
|
return { label: t('statusReady'), tone: 'ok' };
|
|
277
371
|
}
|
|
278
|
-
|
|
279
372
|
"use strict";
|
|
280
373
|
const NS = 'dsh-mobile-access';
|
|
281
374
|
const PANEL_ID = 'mobile-access';
|
|
@@ -416,13 +509,24 @@ function factory(require) {
|
|
|
416
509
|
catch (error) {
|
|
417
510
|
ctx.logger?.warn?.(`[mobile-access] iOS Return newline unavailable: ${String(error)}`);
|
|
418
511
|
}
|
|
512
|
+
try {
|
|
513
|
+
cleanups.push(installIOSInputZoomGuard());
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
ctx.logger?.warn?.(`[mobile-access] iOS input zoom guard unavailable: ${String(error)}`);
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
cleanups.push(installIOSModelKeyboardGuard());
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
ctx.logger?.warn?.(`[mobile-access] iOS model keyboard guard unavailable: ${String(error)}`);
|
|
523
|
+
}
|
|
419
524
|
return () => {
|
|
420
525
|
for (const cleanup of cleanups.reverse()) {
|
|
421
526
|
try {
|
|
422
527
|
cleanup();
|
|
423
528
|
}
|
|
424
529
|
catch {
|
|
425
|
-
// Plugin cleanup must never break the host UI.
|
|
426
530
|
}
|
|
427
531
|
}
|
|
428
532
|
};
|
package/package.json
CHANGED