dsh-mobile 0.1.0-alpha.2 → 0.1.0-alpha.21
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.en.md +123 -0
- package/README.md +86 -116
- package/SECURITY.md +5 -3
- package/assets/brand/app-icon-master.png +0 -0
- package/assets/brand/repository-hero.png +0 -0
- package/cordis.patch.yml +10 -0
- package/lib/cli.js +317 -80
- package/lib/client.js +623 -206
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +31 -1
- package/lib/index.mjs +716 -23
- package/lib/mobile-layout.js +227 -0
- package/lib/mobile-layout.js.map +1 -0
- package/package.json +19 -13
- package/README.zh.md +0 -206
package/lib/client.js
CHANGED
|
@@ -5,81 +5,462 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
//#region src/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
[data-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
[data-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
position:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
background: #fff;
|
|
46
|
-
|
|
47
|
-
|
|
8
|
+
//#region src/native-mobile.ts
|
|
9
|
+
/** Mobile feature and compatibility rules applied to DSH React surfaces. */
|
|
10
|
+
const NATIVE_MOBILE_STYLES = `
|
|
11
|
+
@media (max-width:720px) {
|
|
12
|
+
html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }
|
|
13
|
+
html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }
|
|
14
|
+
html.dsh-native-mobile-active :is(a,button,[role="button"],[role="tab"],[tabindex]) { -webkit-tap-highlight-color:transparent; }
|
|
15
|
+
html.dsh-native-mobile-active[data-dsh-mobile-input="touch"] :is(a,button,[role="button"],[role="tab"],[tabindex]):focus { outline:none !important; }
|
|
16
|
+
[data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }
|
|
17
|
+
[data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }
|
|
18
|
+
[data-dsh-mobile-center] > * { min-width:0 !important; }
|
|
19
|
+
[data-dsh-mobile-header] { min-width:0; padding-right:12px !important; padding-left:50px !important; }
|
|
20
|
+
[data-dsh-mobile-header] [class*="_titleRow"] { box-sizing:border-box !important; min-width:0; min-height:52px !important; height:52px !important; gap:6px !important; padding:4px 8px !important; }
|
|
21
|
+
[data-dsh-mobile-header] [class*="_titleCluster"] { min-width:0; }
|
|
22
|
+
[data-dsh-mobile-header] [class*="_crumbs"] { min-width:0; overflow:hidden; }
|
|
23
|
+
[data-dsh-mobile-header] [class*="_crumb"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
24
|
+
[data-dsh-mobile-header] [class*="_headerActions"] { min-width:0; overflow-x:auto; scrollbar-width:none; }
|
|
25
|
+
[data-dsh-mobile-header] [class*="_headerActions"]::-webkit-scrollbar { display:none; }
|
|
26
|
+
[data-dsh-mobile-header] [class*="_headerUtilities"] { gap:2px !important; }
|
|
27
|
+
[data-dsh-mobile-header] [class*="_sessionLogButton"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }
|
|
28
|
+
[data-dsh-mobile-header] [class*="_sessionLogButton"] > * { display:none !important; }
|
|
29
|
+
[data-dsh-mobile-header] [class*="_sessionLogButton"]::after { color:var(--dsw-text, #171a21); content:"日志"; font-size:11px; font-weight:600; }
|
|
30
|
+
[data-dsh-mobile-header] [class*="_tabs"] { min-height:42px; overflow-x:auto; scrollbar-width:none; }
|
|
31
|
+
[data-dsh-mobile-header] [class*="_tabs"]::-webkit-scrollbar { display:none; }
|
|
32
|
+
[data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }
|
|
33
|
+
[data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:0 auto 0 0 !important; height:100dvh !important; transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease) !important; }
|
|
34
|
+
[data-dsh-mobile-sidebar][data-open="true"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }
|
|
35
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }
|
|
36
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }
|
|
37
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }
|
|
38
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }
|
|
39
|
+
[data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }
|
|
40
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-toggle] svg { display:none !important; }
|
|
41
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-toggle]::after { width:20px; height:2px; border-radius:2px; background:currentColor; box-shadow:0 -6px 0 currentColor,0 6px 0 currentColor; content:""; }
|
|
42
|
+
.dsh-native-mobile-backdrop { position:fixed; z-index:235; inset:0; border:0; background:rgb(15 23 42 / 32%); }
|
|
43
|
+
.dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }
|
|
44
|
+
.dsh-native-mobile-backdrop[hidden] { display:none; }
|
|
45
|
+
[data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); background:var(--dsw-bg, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }
|
|
46
|
+
[data-dsh-mobile-details][data-open="true"] { transform:translateX(0); }
|
|
47
|
+
[data-dsh-mobile-handle] { display:none !important; }
|
|
48
|
+
[data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; animation:dsh-mobile-panel-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }
|
|
49
|
+
[data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-subtle,#e8ebef); }
|
|
50
|
+
[data-dsh-mobile-settings-nav] [class*="_navTitle"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }
|
|
51
|
+
[data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }
|
|
52
|
+
[data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }
|
|
53
|
+
[data-dsh-mobile-settings-list] [class*="_navCell"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }
|
|
54
|
+
[data-dsh-mobile-settings-list] [aria-current="true"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }
|
|
55
|
+
[data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }
|
|
56
|
+
[data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }
|
|
57
|
+
[data-dsh-mobile-settings-header] [class*="_close"] { width:36px !important; height:36px !important; }
|
|
58
|
+
[data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }
|
|
59
|
+
[data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }
|
|
60
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] > [class*="_row"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }
|
|
61
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_rowText"] { width:100% !important; padding-right:0 !important; }
|
|
62
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:44px !important; max-width:100% !important; }
|
|
63
|
+
[data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }
|
|
64
|
+
[data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }
|
|
65
|
+
[data-dsh-mobile-settings-options] [class*="_head"] { min-width:0; flex-wrap:wrap; }
|
|
66
|
+
[data-dsh-mobile-settings-content][data-dsh-mobile-view-transition="true"],
|
|
67
|
+
[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); }
|
|
68
|
+
[data-dsh-mobile-center] textarea { font-size:16px !important; }
|
|
69
|
+
[data-dsh-mobile-center] table { display:block; max-width:100%; overflow-x:auto; }
|
|
70
|
+
[data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }
|
|
71
|
+
[data-dsh-mobile-center] :is(img,video,canvas,svg) { max-width:100%; }
|
|
72
|
+
[data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:12px 16px 20px !important; }
|
|
73
|
+
[data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:14px !important; }
|
|
74
|
+
[data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }
|
|
75
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:44px !important; padding:5px 0 !important; }
|
|
76
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_leading"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }
|
|
77
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_title"] { grid-column:2 !important; grid-row:1 !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
|
|
78
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*="_sep"],[class*="_separator"]) { display:none !important; }
|
|
79
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*="_summary"],[class*="_fileLink"]) { grid-column:2 !important; grid-row:2 !important; width:100% !important; min-width:0 !important; max-width:100% !important; overflow:hidden !important; line-height:20px !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
|
|
80
|
+
[data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_summarySuffix"] { grid-column:2 !important; grid-row:3 !important; margin-left:0 !important; }
|
|
81
|
+
[data-dsh-mobile-message-column] [data-context-fields] > * { display:grid !important; grid-template-columns:minmax(72px,30%) minmax(0,1fr) !important; gap:4px 10px !important; }
|
|
82
|
+
[data-dsh-mobile-message-column] [class*="_ioSection"] { grid-template-columns:1fr !important; row-gap:4px !important; }
|
|
83
|
+
[data-dsh-mobile-message-column] [class*="_body"] { max-width:100% !important; overflow-wrap:anywhere; }
|
|
84
|
+
[data-dsh-mobile-center] [class*="_composer"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }
|
|
85
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_row"] { display:flex !important; align-items:center !important; gap:4px !important; }
|
|
86
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_tools"] { flex:0 0 auto !important; width:auto !important; min-width:0 !important; gap:6px !important; }
|
|
87
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_trailing"] { flex:1 1 auto !important; min-width:0 !important; gap:6px !important; }
|
|
88
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_trailing"] [class*="_root"]:has(button[aria-label^="选择模型"]) { flex:1 1 auto !important; min-width:0 !important; }
|
|
89
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) button[aria-label^="选择模型"] { width:100% !important; min-width:0 !important; padding-left:6px !important; padding-right:4px !important; }
|
|
90
|
+
[data-dsh-mobile-center] [class*="_card"]:has(textarea) button[aria-label^="选择模型"] [class*="_triggerLabel"] { max-width:78px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
91
|
+
[data-dsh-mobile-center] [class*="_root"]:has(> [class*="_card"] textarea) { box-sizing:border-box !important; width:100% !important; padding:0 0 8px !important; }
|
|
92
|
+
[data-dsh-mobile-center] [class*="_root"]:has(> [class*="_card"] textarea) > [class=""]:last-child { display:none !important; }
|
|
93
|
+
.dsh-native-mobile-attach { display:inline-flex; align-items:center; justify-content:center; flex:0 0 44px; width:44px; height:44px; padding:0; border:0; border-radius:12px; background:transparent; color:var(--dsw-text-secondary,#5e6673); cursor:pointer; }
|
|
94
|
+
.dsh-native-mobile-attach:active { background:var(--dsw-bg-subtle,#f1f3f6); }
|
|
95
|
+
.dsh-native-mobile-attach:focus-visible { outline:2px solid var(--dsw-accent,#4c7eea); outline-offset:2px; }
|
|
96
|
+
.dsh-native-mobile-attach:disabled { cursor:not-allowed; opacity:.38; }
|
|
97
|
+
.dsh-native-mobile-attach svg { width:22px; height:22px; }
|
|
98
|
+
.dsh-native-mobile-sheet-mask { position:fixed; z-index:1080; inset:0; border:0; background:rgb(15 23 42 / 32%); }
|
|
99
|
+
.dsh-native-mobile-sheet-mask[hidden] { display:none; }
|
|
100
|
+
.dsh-native-mobile-sheet { position:fixed; z-index:1081; right:0; bottom:0; left:0; display:flex; flex-direction:column; gap:8px; max-height:min(78dvh,680px); padding:8px 16px max(18px,env(safe-area-inset-bottom)); overflow-y:auto; border-radius:22px 22px 0 0; background:var(--dsw-alias-bg-layer-2,#fff); box-shadow:0 -16px 44px rgb(15 23 42 / 16%); animation:dsh-mobile-sheet-in 220ms var(--dsh-mobile-motion-ease); }
|
|
101
|
+
.dsh-native-mobile-sheet[hidden] { display:none; }
|
|
102
|
+
.dsh-native-mobile-sheet__header { position:sticky; z-index:1; top:-8px; display:flex; align-items:center; justify-content:space-between; min-height:48px; padding:8px 0; background:var(--dsw-alias-bg-layer-2,#fff); }
|
|
103
|
+
.dsh-native-mobile-sheet__header strong { font-size:17px; font-weight:600; }
|
|
104
|
+
.dsh-native-mobile-sheet__close { width:44px; height:44px; border:0; border-radius:12px; background:transparent; color:inherit; font-size:24px; }
|
|
105
|
+
.dsh-native-mobile-sheet__path { margin:0 0 4px; overflow-wrap:anywhere; color:var(--dsw-alias-label-secondary,#6b7280); font-size:12px; }
|
|
106
|
+
.dsh-native-mobile-sheet__choice { display:flex; flex-direction:column; align-items:flex-start; gap:2px; width:100%; min-height:56px; padding:10px 14px; border:1px solid var(--dsw-alias-border-subtle,#e2e6eb); border-radius:14px; background:var(--dsw-alias-bg-layer-2,#fff); color:inherit; text-align:left; }
|
|
107
|
+
.dsh-native-mobile-sheet__choice:active { background:var(--dsw-alias-interactive-bg-hover,#f1f3f6); }
|
|
108
|
+
.dsh-native-mobile-sheet__choice small { color:var(--dsw-alias-label-secondary,#6b7280); }
|
|
109
|
+
.dsh-native-mobile-sheet__error { margin:4px 0; color:var(--dsw-alias-status-error,#b42318); font-size:13px; }
|
|
48
110
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
font: inherit;
|
|
59
|
-
font-weight: 700;
|
|
60
|
-
cursor: pointer;
|
|
111
|
+
@keyframes dsh-mobile-fade-in { from { opacity:0; } }
|
|
112
|
+
@keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }
|
|
113
|
+
@keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }
|
|
114
|
+
@keyframes dsh-mobile-sheet-in { from { opacity:.7; transform:translateY(18px); } }
|
|
115
|
+
@media (max-width:420px) {
|
|
116
|
+
[data-dsh-mobile-header] [class*="_headerActions"] { max-width:42vw; }
|
|
117
|
+
[data-dsh-mobile-header] [class*="_titleRow"] { min-height:50px; }
|
|
118
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { align-self:stretch !important; width:100% !important; }
|
|
119
|
+
[data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }
|
|
61
120
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
border-radius: 16px;
|
|
69
|
-
background: #fff;
|
|
70
|
-
box-shadow: 0 10px 30px rgb(15 23 42 / 12%);
|
|
121
|
+
@media (prefers-reduced-motion:reduce) {
|
|
122
|
+
[data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }
|
|
123
|
+
.dsh-native-mobile-backdrop:not([hidden]),[data-dsh-mobile-settings],
|
|
124
|
+
[data-dsh-mobile-settings-content][data-dsh-mobile-view-transition="true"],
|
|
125
|
+
[data-dsh-mobile-view][data-dsh-mobile-view-transition="true"],
|
|
126
|
+
.dsh-native-mobile-sheet { animation:none !important; }
|
|
71
127
|
}
|
|
72
|
-
.dsh-mobile-control__title { margin: 0 0 4px; font-size: 16px; font-weight: 800; }
|
|
73
|
-
.dsh-mobile-control__status { margin: 0 0 14px; color: #475569; overflow-wrap: anywhere; }
|
|
74
|
-
.dsh-mobile-control__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
75
|
-
.dsh-mobile-control__actions button[data-primary='true'] { background: #2563eb; border-color: #2563eb; color: #fff; }
|
|
76
|
-
.dsh-mobile-control[hidden], .dsh-mobile-control__panel[hidden] { display: none; }
|
|
77
128
|
`;
|
|
129
|
+
function classToken(element, suffix) {
|
|
130
|
+
return Array.from(element.classList).some((value) => value.endsWith(suffix));
|
|
131
|
+
}
|
|
132
|
+
function firstByClassSuffix(root, suffix) {
|
|
133
|
+
return Array.from(root.querySelectorAll("[class]")).find((element) => classToken(element, suffix));
|
|
134
|
+
}
|
|
135
|
+
function composerCard(textarea) {
|
|
136
|
+
let candidate = textarea.parentElement;
|
|
137
|
+
while (candidate !== null && !classToken(candidate, "_card")) candidate = candidate.parentElement;
|
|
138
|
+
return candidate ?? void 0;
|
|
139
|
+
}
|
|
140
|
+
function paperclipIcon() {
|
|
141
|
+
const namespace = "http://www.w3.org/2000/svg";
|
|
142
|
+
const svg = document.createElementNS(namespace, "svg");
|
|
143
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
144
|
+
svg.setAttribute("fill", "none");
|
|
145
|
+
svg.setAttribute("stroke", "currentColor");
|
|
146
|
+
svg.setAttribute("stroke-width", "2");
|
|
147
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
148
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
149
|
+
svg.setAttribute("aria-hidden", "true");
|
|
150
|
+
const path = document.createElementNS(namespace, "path");
|
|
151
|
+
path.setAttribute("d", "m21.4 11.6-8.9 8.9a6 6 0 0 1-8.5-8.5l9.6-9.6a4 4 0 0 1 5.7 5.7l-9.7 9.6a2 2 0 0 1-2.8-2.8l8.9-8.9");
|
|
152
|
+
svg.append(path);
|
|
153
|
+
return svg;
|
|
154
|
+
}
|
|
155
|
+
/** Add mobile semantics and phone-local file actions without replacing feature trees. */
|
|
156
|
+
function installNativeMobileSurface() {
|
|
157
|
+
document.documentElement.classList.add("dsh-native-mobile-active");
|
|
158
|
+
const setInputMode = (mode) => {
|
|
159
|
+
document.documentElement.dataset.dshMobileInput = mode;
|
|
160
|
+
};
|
|
161
|
+
const onPointerDown = (event) => {
|
|
162
|
+
if (event.pointerType === "touch" || event.pointerType === "pen") setInputMode("touch");
|
|
163
|
+
};
|
|
164
|
+
const onKeyDown = (event) => {
|
|
165
|
+
if (event.key === "Tab" || event.key.startsWith("Arrow")) setInputMode("keyboard");
|
|
166
|
+
};
|
|
167
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
168
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
169
|
+
const backdrop = document.createElement("button");
|
|
170
|
+
backdrop.type = "button";
|
|
171
|
+
backdrop.className = "dsh-native-mobile-backdrop";
|
|
172
|
+
backdrop.hidden = true;
|
|
173
|
+
backdrop.setAttribute("aria-label", "关闭工作区导航");
|
|
174
|
+
document.body.append(backdrop);
|
|
175
|
+
let frame;
|
|
176
|
+
let sidebar;
|
|
177
|
+
let sidebarRoot;
|
|
178
|
+
let toggle;
|
|
179
|
+
let viewArea;
|
|
180
|
+
let scheduled = 0;
|
|
181
|
+
let transitionFrame = 0;
|
|
182
|
+
let transitionRestartFrame = 0;
|
|
183
|
+
let transitionTimer = 0;
|
|
184
|
+
let transitionTarget;
|
|
185
|
+
const fileInput = document.createElement("input");
|
|
186
|
+
fileInput.type = "file";
|
|
187
|
+
fileInput.accept = "image/*";
|
|
188
|
+
fileInput.multiple = true;
|
|
189
|
+
fileInput.hidden = true;
|
|
190
|
+
fileInput.setAttribute("aria-hidden", "true");
|
|
191
|
+
const attach = document.createElement("button");
|
|
192
|
+
attach.type = "button";
|
|
193
|
+
attach.className = "dsh-native-mobile-attach";
|
|
194
|
+
attach.setAttribute("aria-label", "添加图片");
|
|
195
|
+
attach.setAttribute("title", "添加图片");
|
|
196
|
+
attach.append(paperclipIcon());
|
|
197
|
+
document.body.append(fileInput);
|
|
198
|
+
const sheetMask = document.createElement("button");
|
|
199
|
+
sheetMask.type = "button";
|
|
200
|
+
sheetMask.className = "dsh-native-mobile-sheet-mask";
|
|
201
|
+
sheetMask.hidden = true;
|
|
202
|
+
sheetMask.setAttribute("aria-label", "关闭图片选择");
|
|
203
|
+
const sheet = document.createElement("section");
|
|
204
|
+
sheet.className = "dsh-native-mobile-sheet";
|
|
205
|
+
sheet.hidden = true;
|
|
206
|
+
sheet.setAttribute("aria-label", "添加图片");
|
|
207
|
+
document.body.append(sheetMask, sheet);
|
|
208
|
+
const handFilesToComposer = (files) => {
|
|
209
|
+
const textarea = document.querySelector("[data-dsh-mobile-center] textarea");
|
|
210
|
+
if (textarea === null || files.length === 0) return;
|
|
211
|
+
const paste = new Event("paste", {
|
|
212
|
+
bubbles: true,
|
|
213
|
+
cancelable: true
|
|
214
|
+
});
|
|
215
|
+
Object.defineProperty(paste, "clipboardData", { value: Object.freeze({
|
|
216
|
+
files,
|
|
217
|
+
items: Object.freeze(files.map((file) => Object.freeze({
|
|
218
|
+
kind: "file",
|
|
219
|
+
type: file.type,
|
|
220
|
+
getAsFile: () => file
|
|
221
|
+
}))),
|
|
222
|
+
getData: () => ""
|
|
223
|
+
}) });
|
|
224
|
+
textarea.dispatchEvent(paste);
|
|
225
|
+
};
|
|
226
|
+
const closeSheet = () => {
|
|
227
|
+
sheet.hidden = true;
|
|
228
|
+
sheetMask.hidden = true;
|
|
229
|
+
sheet.replaceChildren();
|
|
230
|
+
};
|
|
231
|
+
const sheetHeader = (title) => {
|
|
232
|
+
const header = document.createElement("header");
|
|
233
|
+
header.className = "dsh-native-mobile-sheet__header";
|
|
234
|
+
const heading = document.createElement("strong");
|
|
235
|
+
heading.textContent = title;
|
|
236
|
+
const close = document.createElement("button");
|
|
237
|
+
close.type = "button";
|
|
238
|
+
close.className = "dsh-native-mobile-sheet__close";
|
|
239
|
+
close.setAttribute("aria-label", "关闭");
|
|
240
|
+
close.textContent = "×";
|
|
241
|
+
close.addEventListener("click", closeSheet);
|
|
242
|
+
header.append(heading, close);
|
|
243
|
+
sheet.append(header);
|
|
244
|
+
};
|
|
245
|
+
const choice = (title, detail, action) => {
|
|
246
|
+
const button = document.createElement("button");
|
|
247
|
+
button.type = "button";
|
|
248
|
+
button.className = "dsh-native-mobile-sheet__choice";
|
|
249
|
+
const label = document.createElement("span");
|
|
250
|
+
label.textContent = title;
|
|
251
|
+
const hint = document.createElement("small");
|
|
252
|
+
hint.textContent = detail;
|
|
253
|
+
button.append(label, hint);
|
|
254
|
+
button.addEventListener("click", action);
|
|
255
|
+
return button;
|
|
256
|
+
};
|
|
257
|
+
const showSheet = () => {
|
|
258
|
+
sheet.hidden = false;
|
|
259
|
+
sheetMask.hidden = false;
|
|
260
|
+
};
|
|
261
|
+
const openComputerImages = async (path) => {
|
|
262
|
+
sheet.replaceChildren();
|
|
263
|
+
sheetHeader("电脑图片");
|
|
264
|
+
const progress = document.createElement("p");
|
|
265
|
+
progress.className = "dsh-native-mobile-sheet__path";
|
|
266
|
+
progress.textContent = "正在读取电脑目录…";
|
|
267
|
+
sheet.append(progress);
|
|
268
|
+
showSheet();
|
|
269
|
+
try {
|
|
270
|
+
const suffix = path === void 0 ? "" : `?path=${encodeURIComponent(path)}`;
|
|
271
|
+
const response = await fetch(`/mobile-access/computer-images${suffix}`, {
|
|
272
|
+
credentials: "same-origin",
|
|
273
|
+
cache: "no-store"
|
|
274
|
+
});
|
|
275
|
+
if (!response.ok) throw new Error(`读取失败(${String(response.status)})`);
|
|
276
|
+
const listing = await response.json();
|
|
277
|
+
progress.textContent = listing.path;
|
|
278
|
+
if (listing.parent !== void 0) sheet.append(choice("返回上一级", listing.parent, () => {
|
|
279
|
+
openComputerImages(listing.parent);
|
|
280
|
+
}));
|
|
281
|
+
for (const entry of listing.entries) {
|
|
282
|
+
if (entry.kind === "directory") {
|
|
283
|
+
sheet.append(choice(entry.name, "文件夹", () => {
|
|
284
|
+
openComputerImages(entry.path);
|
|
285
|
+
}));
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const row = choice(entry.name, "添加这张电脑图片", () => {
|
|
289
|
+
row.disabled = true;
|
|
290
|
+
fetch(`/mobile-access/computer-image?path=${encodeURIComponent(entry.path)}`, {
|
|
291
|
+
credentials: "same-origin",
|
|
292
|
+
cache: "no-store"
|
|
293
|
+
}).then(async (response) => {
|
|
294
|
+
if (!response.ok) throw new Error(`读取失败(${String(response.status)})`);
|
|
295
|
+
const blob = await response.blob();
|
|
296
|
+
handFilesToComposer([new File([blob], entry.name, { type: blob.type })]);
|
|
297
|
+
closeSheet();
|
|
298
|
+
}).catch((error) => {
|
|
299
|
+
row.disabled = false;
|
|
300
|
+
progress.className = "dsh-native-mobile-sheet__error";
|
|
301
|
+
progress.textContent = error instanceof Error ? error.message : String(error);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
sheet.append(row);
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
progress.className = "dsh-native-mobile-sheet__error";
|
|
308
|
+
progress.textContent = error instanceof Error ? error.message : String(error);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
const openAttachmentSources = () => {
|
|
312
|
+
sheet.replaceChildren();
|
|
313
|
+
sheetHeader("添加图片");
|
|
314
|
+
sheet.append(choice("从手机选择", "相册、相机或手机存储", () => {
|
|
315
|
+
closeSheet();
|
|
316
|
+
fileInput.click();
|
|
317
|
+
}), choice("从电脑选择", "在手机页面浏览电脑中的图片", () => {
|
|
318
|
+
openComputerImages();
|
|
319
|
+
}));
|
|
320
|
+
showSheet();
|
|
321
|
+
};
|
|
322
|
+
attach.addEventListener("click", openAttachmentSources);
|
|
323
|
+
sheetMask.addEventListener("click", closeSheet);
|
|
324
|
+
fileInput.addEventListener("change", () => {
|
|
325
|
+
handFilesToComposer([...fileInput.files ?? []]);
|
|
326
|
+
fileInput.value = "";
|
|
327
|
+
});
|
|
328
|
+
const animateNavigation = (event) => {
|
|
329
|
+
if (!(event.target instanceof Element)) return;
|
|
330
|
+
const trigger = event.target.closest("button,a,[role=\"tab\"],[aria-selected]");
|
|
331
|
+
if (trigger === null || trigger.hasAttribute("disabled") || trigger.getAttribute("aria-disabled") === "true") return;
|
|
332
|
+
if (trigger.getAttribute("aria-selected") === "true" || trigger.getAttribute("aria-current") === "true") return;
|
|
333
|
+
const settingsNavigation = trigger.closest("[data-dsh-mobile-settings-list]") !== null;
|
|
334
|
+
const conversationNavigation = trigger.matches("[role=\"tab\"]");
|
|
335
|
+
const sidebarNavigation = trigger.closest("[data-dsh-mobile-sidebar-root]") !== null && trigger.closest("[data-dsh-mobile-toggle]") === null;
|
|
336
|
+
if (!settingsNavigation && !conversationNavigation && !sidebarNavigation) return;
|
|
337
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
338
|
+
if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame);
|
|
339
|
+
if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame);
|
|
340
|
+
transitionFrame = requestAnimationFrame(() => {
|
|
341
|
+
transitionFrame = 0;
|
|
342
|
+
const target = settingsNavigation ? document.querySelector("[data-dsh-mobile-settings-content]") : viewArea;
|
|
343
|
+
if (target === null || target === void 0) return;
|
|
344
|
+
transitionTarget?.removeAttribute("data-dsh-mobile-view-transition");
|
|
345
|
+
target.removeAttribute("data-dsh-mobile-view-transition");
|
|
346
|
+
transitionRestartFrame = requestAnimationFrame(() => {
|
|
347
|
+
transitionRestartFrame = 0;
|
|
348
|
+
transitionTarget = target;
|
|
349
|
+
target.dataset.dshMobileViewTransition = "true";
|
|
350
|
+
if (transitionTimer !== 0) clearTimeout(transitionTimer);
|
|
351
|
+
transitionTimer = window.setTimeout(() => {
|
|
352
|
+
target.removeAttribute("data-dsh-mobile-view-transition");
|
|
353
|
+
if (transitionTarget === target) transitionTarget = void 0;
|
|
354
|
+
transitionTimer = 0;
|
|
355
|
+
}, 240);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
document.addEventListener("click", animateNavigation);
|
|
360
|
+
const sync = () => {
|
|
361
|
+
scheduled = 0;
|
|
362
|
+
frame = firstByClassSuffix(document, "_frame");
|
|
363
|
+
const dedicatedCenter = document.querySelector(".dshm-main") ?? void 0;
|
|
364
|
+
if (frame !== void 0) frame.dataset.dshMobileFrame = "true";
|
|
365
|
+
sidebar = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_sidebarCol");
|
|
366
|
+
const center = frame === void 0 ? dedicatedCenter : firstByClassSuffix(frame, "_centerCol");
|
|
367
|
+
const details = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_detailsCol");
|
|
368
|
+
const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
|
|
369
|
+
if (center === void 0) return;
|
|
370
|
+
if (center !== void 0) {
|
|
371
|
+
center.dataset.dshMobileCenter = "true";
|
|
372
|
+
center.querySelector("header")?.setAttribute("data-dsh-mobile-header", "true");
|
|
373
|
+
viewArea = firstByClassSuffix(center, "_viewArea");
|
|
374
|
+
if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
|
|
375
|
+
const conversation = center.querySelector("[data-conversation-scroll]");
|
|
376
|
+
const messageColumn = conversation === null ? void 0 : firstByClassSuffix(conversation, "_column");
|
|
377
|
+
const messageScroll = messageColumn?.parentElement;
|
|
378
|
+
if (messageColumn !== void 0 && messageScroll !== null && messageScroll !== void 0 && classToken(messageScroll, "_scroll")) {
|
|
379
|
+
messageColumn.dataset.dshMobileMessageColumn = "true";
|
|
380
|
+
messageScroll.dataset.dshMobileMessageScroll = "true";
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (handle !== void 0) handle.dataset.dshMobileHandle = "true";
|
|
384
|
+
if (details !== void 0) {
|
|
385
|
+
details.dataset.dshMobileDetails = "true";
|
|
386
|
+
const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
|
|
387
|
+
details.dataset.open = String(lastColumn !== void 0 && lastColumn !== "0px" && lastColumn !== "0");
|
|
388
|
+
}
|
|
389
|
+
const settings = Array.from(document.querySelectorAll("[role=\"dialog\"]")).find((dialog) => {
|
|
390
|
+
return Array.from(dialog.children).find((child) => child instanceof HTMLElement && classToken(child, "_nav")) !== void 0;
|
|
391
|
+
});
|
|
392
|
+
if (settings !== void 0) {
|
|
393
|
+
settings.dataset.dshMobileSettings = "true";
|
|
394
|
+
const nav = Array.from(settings.children).find((child) => child instanceof HTMLElement && classToken(child, "_nav"));
|
|
395
|
+
const content = Array.from(settings.children).find((child) => child instanceof HTMLElement && classToken(child, "_content"));
|
|
396
|
+
if (nav !== void 0) {
|
|
397
|
+
nav.dataset.dshMobileSettingsNav = "true";
|
|
398
|
+
firstByClassSuffix(nav, "_navList")?.setAttribute("data-dsh-mobile-settings-list", "true");
|
|
399
|
+
}
|
|
400
|
+
if (content !== void 0) {
|
|
401
|
+
content.dataset.dshMobileSettingsContent = "true";
|
|
402
|
+
firstByClassSuffix(content, "_header")?.setAttribute("data-dsh-mobile-settings-header", "true");
|
|
403
|
+
firstByClassSuffix(content, "_options")?.setAttribute("data-dsh-mobile-settings-options", "true");
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const textarea = center?.querySelector("textarea");
|
|
407
|
+
const card = textarea === null || textarea === void 0 ? void 0 : composerCard(textarea);
|
|
408
|
+
const tools = card === void 0 ? void 0 : firstByClassSuffix(card, "_tools");
|
|
409
|
+
attach.disabled = textarea === void 0 || textarea === null || textarea.disabled || textarea.readOnly;
|
|
410
|
+
if (tools !== void 0 && attach.parentElement !== tools) tools.append(attach);
|
|
411
|
+
if (sidebar === void 0) return;
|
|
412
|
+
sidebar.dataset.dshMobileSidebar = "true";
|
|
413
|
+
toggle = firstByClassSuffix(sidebar, "_toggle");
|
|
414
|
+
let candidate = toggle?.parentElement;
|
|
415
|
+
while (candidate !== void 0 && candidate !== null && candidate !== sidebar && !classToken(candidate, "_root")) candidate = candidate.parentElement;
|
|
416
|
+
sidebarRoot = candidate !== sidebar && candidate !== null ? candidate : void 0;
|
|
417
|
+
if (sidebarRoot === void 0) return;
|
|
418
|
+
sidebarRoot.dataset.dshMobileSidebarRoot = "true";
|
|
419
|
+
if (toggle !== void 0) toggle.dataset.dshMobileToggle = "true";
|
|
420
|
+
const collapsed = classToken(sidebarRoot, "_collapsed");
|
|
421
|
+
sidebar.dataset.open = String(!collapsed);
|
|
422
|
+
backdrop.hidden = collapsed;
|
|
423
|
+
};
|
|
424
|
+
const schedule = () => {
|
|
425
|
+
if (scheduled === 0) scheduled = requestAnimationFrame(sync);
|
|
426
|
+
};
|
|
427
|
+
const observer = new MutationObserver(schedule);
|
|
428
|
+
observer.observe(document.body, {
|
|
429
|
+
childList: true,
|
|
430
|
+
subtree: true,
|
|
431
|
+
attributes: true,
|
|
432
|
+
attributeFilter: ["class", "style"]
|
|
433
|
+
});
|
|
434
|
+
backdrop.addEventListener("click", () => {
|
|
435
|
+
if (sidebar?.dataset.open === "true") toggle?.click();
|
|
436
|
+
});
|
|
437
|
+
sync();
|
|
438
|
+
return () => {
|
|
439
|
+
observer.disconnect();
|
|
440
|
+
if (scheduled !== 0) cancelAnimationFrame(scheduled);
|
|
441
|
+
if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame);
|
|
442
|
+
if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame);
|
|
443
|
+
if (transitionTimer !== 0) clearTimeout(transitionTimer);
|
|
444
|
+
transitionTarget?.removeAttribute("data-dsh-mobile-view-transition");
|
|
445
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
446
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
447
|
+
document.removeEventListener("click", animateNavigation);
|
|
448
|
+
backdrop.remove();
|
|
449
|
+
attach.remove();
|
|
450
|
+
fileInput.remove();
|
|
451
|
+
sheet.remove();
|
|
452
|
+
sheetMask.remove();
|
|
453
|
+
document.documentElement.classList.remove("dsh-native-mobile-active");
|
|
454
|
+
delete document.documentElement.dataset.dshMobileInput;
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/client.ts
|
|
78
459
|
function isLoopbackHost(hostname) {
|
|
79
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "
|
|
460
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
80
461
|
}
|
|
81
|
-
function element(
|
|
82
|
-
const node = document.createElement(
|
|
462
|
+
function element(tag, className) {
|
|
463
|
+
const node = document.createElement(tag);
|
|
83
464
|
if (className !== void 0) node.className = className;
|
|
84
465
|
return node;
|
|
85
466
|
}
|
|
@@ -95,63 +476,64 @@ window.__ModuleLoader__.load({
|
|
|
95
476
|
if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : `HTTP ${String(response.status)}`);
|
|
96
477
|
return body;
|
|
97
478
|
}
|
|
98
|
-
function mobileExtensionRequest(path, init = {}) {
|
|
99
|
-
const target = new URL(path, location.href);
|
|
100
|
-
if (target.origin !== location.origin) throw new TypeError("mobile extension requests must stay on the DSH origin");
|
|
101
|
-
const headers = new Headers(init.headers);
|
|
102
|
-
const method = (init.method ?? "GET").toUpperCase();
|
|
103
|
-
if (method !== "GET" && method !== "HEAD" && !headers.has("x-dsh-mobile-csrf")) {
|
|
104
|
-
const csrf = document.cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith("dsh_ma_csrf="))?.slice(12);
|
|
105
|
-
if (csrf !== void 0) headers.set("x-dsh-mobile-csrf", csrf);
|
|
106
|
-
}
|
|
107
|
-
return fetch(target, {
|
|
108
|
-
...init,
|
|
109
|
-
cache: "no-store",
|
|
110
|
-
credentials: "same-origin",
|
|
111
|
-
headers,
|
|
112
|
-
redirect: "error"
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
479
|
function installControl() {
|
|
116
|
-
const zh = navigator.language.toLowerCase().startsWith("zh");
|
|
117
480
|
const root = element("div", "dsh-mobile-control");
|
|
118
481
|
const panel = element("section", "dsh-mobile-control__panel");
|
|
119
482
|
panel.hidden = true;
|
|
120
|
-
|
|
121
|
-
|
|
483
|
+
panel.setAttribute("aria-label", "移动访问");
|
|
484
|
+
const header = element("header", "dsh-mobile-control__header");
|
|
485
|
+
const title = element("h2");
|
|
486
|
+
title.textContent = "移动访问";
|
|
487
|
+
const close = element("button", "dsh-mobile-control__close");
|
|
488
|
+
close.type = "button";
|
|
489
|
+
close.textContent = "×";
|
|
490
|
+
close.setAttribute("aria-label", "收起移动访问");
|
|
491
|
+
const access = element("div", "dsh-mobile-control__access");
|
|
492
|
+
access.hidden = true;
|
|
493
|
+
const accessLabel = element("span", "dsh-mobile-control__access-label");
|
|
494
|
+
accessLabel.textContent = "浏览器访问";
|
|
495
|
+
const accessLink = element("a", "dsh-mobile-control__access-link");
|
|
496
|
+
accessLink.target = "_blank";
|
|
497
|
+
accessLink.rel = "noreferrer";
|
|
498
|
+
access.append(accessLabel, accessLink);
|
|
122
499
|
const status = element("p", "dsh-mobile-control__status");
|
|
123
|
-
status.textContent =
|
|
500
|
+
status.textContent = "正在读取状态…";
|
|
124
501
|
const actions = element("div", "dsh-mobile-control__actions");
|
|
125
|
-
const toggle = element("button");
|
|
126
|
-
toggle.
|
|
127
|
-
const pair = element("button");
|
|
128
|
-
pair.
|
|
502
|
+
const toggle = element("button", "dsh-mobile-control__secondary");
|
|
503
|
+
toggle.type = "button";
|
|
504
|
+
const pair = element("button", "dsh-mobile-control__primary");
|
|
505
|
+
pair.type = "button";
|
|
506
|
+
pair.textContent = "生成并复制密钥";
|
|
507
|
+
header.append(title, close);
|
|
129
508
|
actions.append(toggle, pair);
|
|
130
|
-
panel.append(
|
|
509
|
+
panel.append(header, access, status, actions);
|
|
131
510
|
root.append(panel);
|
|
132
511
|
document.body.append(root);
|
|
133
512
|
let running = false;
|
|
513
|
+
let origin = "";
|
|
514
|
+
const setOpen = (open) => {
|
|
515
|
+
panel.hidden = !open;
|
|
516
|
+
for (const trigger of document.querySelectorAll(".dsh-mobile-control__trigger")) trigger.setAttribute("aria-expanded", String(open));
|
|
517
|
+
};
|
|
134
518
|
const render = (data) => {
|
|
135
519
|
running = data.running === true;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
520
|
+
origin = running && typeof data.origin === "string" ? data.origin : "";
|
|
521
|
+
access.hidden = origin === "";
|
|
522
|
+
accessLink.href = origin;
|
|
523
|
+
accessLink.textContent = origin;
|
|
524
|
+
accessLink.title = origin;
|
|
525
|
+
status.classList.toggle("is-running", running);
|
|
526
|
+
status.textContent = running ? "移动访问已开启。" : "已关闭。DSH 仍只在本机可用。";
|
|
527
|
+
toggle.textContent = running ? "关闭移动访问" : "开启移动访问";
|
|
139
528
|
pair.disabled = !running;
|
|
140
529
|
};
|
|
141
|
-
const refresh = async () => {
|
|
142
|
-
try {
|
|
143
|
-
render(await requestJson("/api/mobile-access/control"));
|
|
144
|
-
} catch (error) {
|
|
145
|
-
status.textContent = error instanceof Error ? error.message : String(error);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
530
|
toggle.addEventListener("click", () => {
|
|
149
531
|
toggle.disabled = true;
|
|
150
532
|
requestJson("/api/mobile-access/control", {
|
|
151
533
|
method: "POST",
|
|
152
534
|
body: JSON.stringify({ running: !running })
|
|
153
535
|
}).then(render, (error) => {
|
|
154
|
-
status.textContent =
|
|
536
|
+
status.textContent = String(error);
|
|
155
537
|
}).finally(() => {
|
|
156
538
|
toggle.disabled = false;
|
|
157
539
|
});
|
|
@@ -162,154 +544,189 @@ window.__ModuleLoader__.load({
|
|
|
162
544
|
method: "POST",
|
|
163
545
|
body: "{}"
|
|
164
546
|
}).then(async (data) => {
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
|
|
547
|
+
const key = typeof data.appKey === "string" ? data.appKey : "";
|
|
548
|
+
if (key === "") {
|
|
549
|
+
status.textContent = "无法生成配对密钥。";
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
await navigator.clipboard.writeText(key);
|
|
554
|
+
status.textContent = "配对密钥已复制,请粘贴到 Android App。";
|
|
555
|
+
} catch {
|
|
556
|
+
status.textContent = `请复制配对密钥:${key}`;
|
|
557
|
+
status.classList.add("is-key");
|
|
558
|
+
}
|
|
168
559
|
}, (error) => {
|
|
169
|
-
status.textContent =
|
|
560
|
+
status.textContent = String(error);
|
|
170
561
|
}).finally(() => {
|
|
171
562
|
pair.disabled = !running;
|
|
172
563
|
});
|
|
173
564
|
});
|
|
174
|
-
|
|
565
|
+
close.addEventListener("click", () => {
|
|
566
|
+
setOpen(false);
|
|
567
|
+
});
|
|
568
|
+
const dismiss = (event) => {
|
|
569
|
+
if (panel.hidden || !(event.target instanceof Node)) return;
|
|
570
|
+
if (!panel.contains(event.target) && !document.querySelector(".dsh-mobile-control__trigger")?.contains(event.target)) setOpen(false);
|
|
571
|
+
};
|
|
572
|
+
document.addEventListener("pointerdown", dismiss);
|
|
573
|
+
requestJson("/api/mobile-access/control").then(render, (error) => {
|
|
574
|
+
status.textContent = String(error);
|
|
575
|
+
});
|
|
175
576
|
return {
|
|
176
577
|
remove: () => {
|
|
578
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
177
579
|
root.remove();
|
|
178
580
|
},
|
|
179
581
|
toggle: () => {
|
|
180
|
-
panel.hidden
|
|
582
|
+
setOpen(panel.hidden !== false);
|
|
181
583
|
}
|
|
182
584
|
};
|
|
183
585
|
}
|
|
184
|
-
function
|
|
586
|
+
function mobileRequest(path, init = {}) {
|
|
587
|
+
const target = new URL(path, location.href);
|
|
588
|
+
if (target.origin !== location.origin) throw new TypeError("mobile extension requests must stay on the DSH origin");
|
|
589
|
+
const headers = new Headers(init.headers);
|
|
590
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
591
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
592
|
+
const csrf = document.cookie.split(";").map((value) => value.trim()).find((value) => value.startsWith("dsh_ma_csrf="))?.slice(12);
|
|
593
|
+
if (csrf !== void 0) headers.set("x-dsh-mobile-csrf", csrf);
|
|
594
|
+
}
|
|
595
|
+
return fetch(target, {
|
|
596
|
+
...init,
|
|
597
|
+
headers,
|
|
598
|
+
credentials: "same-origin",
|
|
599
|
+
cache: "no-store",
|
|
600
|
+
redirect: "error"
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function installCustomAssets() {
|
|
604
|
+
const style = element("style");
|
|
605
|
+
style.dataset.plugin = "dsh-mobile-custom";
|
|
606
|
+
document.head.append(style);
|
|
607
|
+
let cssLoading = false;
|
|
608
|
+
const refreshCss = async () => {
|
|
609
|
+
if (cssLoading || document.visibilityState === "hidden") return;
|
|
610
|
+
cssLoading = true;
|
|
611
|
+
try {
|
|
612
|
+
const response = await fetch("/mobile-access/custom.css", {
|
|
613
|
+
cache: "no-store",
|
|
614
|
+
credentials: "same-origin"
|
|
615
|
+
});
|
|
616
|
+
if (response.ok) {
|
|
617
|
+
const css = await response.text();
|
|
618
|
+
if (style.textContent !== css) style.textContent = css;
|
|
619
|
+
}
|
|
620
|
+
} finally {
|
|
621
|
+
cssLoading = false;
|
|
622
|
+
}
|
|
623
|
+
};
|
|
185
624
|
let activeRoot;
|
|
186
|
-
let
|
|
187
|
-
let
|
|
188
|
-
let
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (typeof mount !== "function") throw new TypeError("dshMobile.register requires a mount function");
|
|
193
|
-
pendingMount = mount;
|
|
625
|
+
let disposeActive;
|
|
626
|
+
let source = "";
|
|
627
|
+
let pending;
|
|
628
|
+
const previous = window.dshMobile;
|
|
629
|
+
window.dshMobile = Object.freeze({ register: (mount) => {
|
|
630
|
+
pending = mount;
|
|
194
631
|
} });
|
|
195
|
-
const registeredMount = () =>
|
|
196
|
-
|
|
197
|
-
const refresh = async () => {
|
|
198
|
-
if (loading || document.visibilityState === "hidden") return;
|
|
199
|
-
loading = true;
|
|
632
|
+
const registeredMount = () => pending;
|
|
633
|
+
const refreshJs = async () => {
|
|
200
634
|
try {
|
|
201
635
|
const response = await fetch("/mobile-access/custom.js", {
|
|
202
636
|
cache: "no-store",
|
|
203
|
-
credentials: "same-origin"
|
|
204
|
-
redirect: "error"
|
|
637
|
+
credentials: "same-origin"
|
|
205
638
|
});
|
|
206
|
-
if (!response.ok
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const script =
|
|
212
|
-
script.textContent = `${
|
|
639
|
+
if (!response.ok) return;
|
|
640
|
+
const next = await response.text();
|
|
641
|
+
if (next === source) return;
|
|
642
|
+
source = next;
|
|
643
|
+
pending = void 0;
|
|
644
|
+
const script = element("script");
|
|
645
|
+
script.textContent = `${next}\n//# sourceURL=dsh-mobile-custom.js`;
|
|
213
646
|
document.head.append(script);
|
|
214
647
|
script.remove();
|
|
215
648
|
const mount = registeredMount();
|
|
216
649
|
if (mount === void 0) return;
|
|
217
|
-
const nextRoot =
|
|
650
|
+
const nextRoot = element("div");
|
|
218
651
|
nextRoot.dataset.dshMobileExtension = "true";
|
|
219
652
|
document.body.append(nextRoot);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
activeDispose = typeof nextDispose === "function" ? nextDispose : void 0;
|
|
232
|
-
} catch (error) {
|
|
233
|
-
nextRoot.remove();
|
|
234
|
-
console.error("DSH Mobile custom extension failed to mount", error);
|
|
235
|
-
}
|
|
236
|
-
} catch {} finally {
|
|
237
|
-
loading = false;
|
|
238
|
-
}
|
|
653
|
+
const nextDispose = mount({
|
|
654
|
+
document,
|
|
655
|
+
request: mobileRequest,
|
|
656
|
+
root: nextRoot,
|
|
657
|
+
window
|
|
658
|
+
});
|
|
659
|
+
disposeActive?.();
|
|
660
|
+
activeRoot?.remove();
|
|
661
|
+
activeRoot = nextRoot;
|
|
662
|
+
disposeActive = typeof nextDispose === "function" ? nextDispose : void 0;
|
|
663
|
+
} catch {}
|
|
239
664
|
};
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
665
|
+
refreshCss();
|
|
666
|
+
refreshJs();
|
|
667
|
+
const timer = window.setInterval(() => {
|
|
668
|
+
refreshCss();
|
|
669
|
+
refreshJs();
|
|
243
670
|
}, 1e3);
|
|
244
671
|
return () => {
|
|
245
672
|
clearInterval(timer);
|
|
246
|
-
|
|
673
|
+
disposeActive?.();
|
|
247
674
|
activeRoot?.remove();
|
|
248
|
-
|
|
249
|
-
|
|
675
|
+
style.remove();
|
|
676
|
+
if (previous === void 0) delete window.dshMobile;
|
|
677
|
+
else window.dshMobile = previous;
|
|
250
678
|
};
|
|
251
679
|
}
|
|
252
|
-
|
|
680
|
+
const CONTROL_STYLES = `
|
|
681
|
+
.dsh-mobile-control{position:fixed;z-index:1000;left:16px;bottom:64px;font:14px/1.45 system-ui;color:var(--dsw-alias-label-primary,#16181d)}
|
|
682
|
+
.dsh-mobile-control__panel{box-sizing:border-box;width:min(328px,calc(100vw - 32px));padding:16px;border:1px solid var(--dsw-alias-border-subtle,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}
|
|
683
|
+
.dsh-mobile-control__header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.dsh-mobile-control__panel h2{margin:0;font-size:17px;line-height:24px}.dsh-mobile-control__close{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:transparent;color:inherit;font-size:24px;line-height:1;cursor:pointer}.dsh-mobile-control__close:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}
|
|
684
|
+
.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:":"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:#2563eb;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}
|
|
685
|
+
.dsh-mobile-control__status{margin:0 0 14px;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,#606873)}.dsh-mobile-control__status::before{display:inline-block;width:8px;height:8px;margin-right:7px;border-radius:50%;background:#98a1ad;content:""}.dsh-mobile-control__status.is-running::before{background:#16a36a}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}
|
|
686
|
+
.dsh-mobile-control__actions{display:flex;flex-wrap:wrap;gap:8px}.dsh-mobile-control__actions button{min-height:40px;padding:8px 12px;border-radius:11px;font:inherit;cursor:pointer}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
687
|
+
.dsh-mobile-control__trigger{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px;padding:6px 2px 6px 10px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font:14px/22px system-ui;cursor:pointer}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger.is-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;border-radius:50%}.dsh-mobile-control__trigger-icon{position:relative;box-sizing:border-box;flex:none;width:14px;height:19px;border:1.7px solid currentColor;border-radius:3px}.dsh-mobile-control__trigger-icon::after{position:absolute;right:4px;bottom:2px;width:4px;height:1.5px;border-radius:2px;background:currentColor;content:""}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
688
|
+
`;
|
|
689
|
+
/** Mount the desktop control or mobile feature enhancements. */
|
|
253
690
|
function apply(ctx) {
|
|
254
691
|
ctx.effect(() => {
|
|
255
|
-
const
|
|
692
|
+
const loopback = isLoopbackHost(location.hostname) && !new URLSearchParams(location.search).has("dsh-mobile-preview");
|
|
693
|
+
const style = element("style");
|
|
256
694
|
style.dataset.plugin = "dsh-mobile";
|
|
257
|
-
style.textContent =
|
|
695
|
+
style.textContent = loopback ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
|
|
258
696
|
document.head.append(style);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const refreshCustomCss = async () => {
|
|
267
|
-
if (customLoading || document.visibilityState === "hidden") return;
|
|
268
|
-
customLoading = true;
|
|
269
|
-
try {
|
|
270
|
-
const response = await fetch("/mobile-access/custom.css", {
|
|
271
|
-
cache: "no-store",
|
|
272
|
-
credentials: "same-origin",
|
|
273
|
-
redirect: "error"
|
|
274
|
-
});
|
|
275
|
-
if (!response.ok || !response.headers.get("content-type")?.startsWith("text/css")) return;
|
|
276
|
-
const css = await response.text();
|
|
277
|
-
if (custom.textContent !== css) custom.textContent = css;
|
|
278
|
-
} catch {} finally {
|
|
279
|
-
customLoading = false;
|
|
280
|
-
}
|
|
697
|
+
if (!loopback) {
|
|
698
|
+
const removeCustom = installCustomAssets();
|
|
699
|
+
const removeSurface = installNativeMobileSurface();
|
|
700
|
+
return () => {
|
|
701
|
+
removeCustom();
|
|
702
|
+
removeSurface();
|
|
703
|
+
style.remove();
|
|
281
704
|
};
|
|
282
|
-
refreshCustomCss();
|
|
283
|
-
customRefresh = setInterval(() => {
|
|
284
|
-
refreshCustomCss();
|
|
285
|
-
}, 1e3);
|
|
286
705
|
}
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
const disposeSlot = control === void 0 ? void 0 : ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
706
|
+
const control = installControl();
|
|
707
|
+
const disposeSlot = ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
290
708
|
name: "sidebar.footer.action",
|
|
291
709
|
id: "dsh-mobile"
|
|
292
710
|
}, ({ wide }) => (0, react.createElement)("button", {
|
|
293
|
-
|
|
711
|
+
"aria-expanded": false,
|
|
712
|
+
"aria-label": "移动访问",
|
|
713
|
+
className: `dsh-mobile-control__trigger${wide ? "" : " is-rail"}`,
|
|
294
714
|
type: "button",
|
|
295
|
-
title:
|
|
715
|
+
title: "移动访问",
|
|
296
716
|
onClick: control.toggle
|
|
297
|
-
},
|
|
717
|
+
}, (0, react.createElement)("span", {
|
|
718
|
+
"aria-hidden": true,
|
|
719
|
+
className: "dsh-mobile-control__trigger-icon"
|
|
720
|
+
}), wide ? (0, react.createElement)("span", { className: "dsh-mobile-control__trigger-label" }, "移动访问") : void 0)));
|
|
298
721
|
return () => {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
control?.remove();
|
|
302
|
-
removeCustomExtension?.();
|
|
303
|
-
custom?.remove();
|
|
722
|
+
disposeSlot();
|
|
723
|
+
control.remove();
|
|
304
724
|
style.remove();
|
|
305
725
|
};
|
|
306
|
-
}, "dsh-mobile:
|
|
726
|
+
}, "dsh-mobile: stock mobile adaptation and local control");
|
|
307
727
|
}
|
|
308
|
-
/** Client
|
|
728
|
+
/** Client services required by the mobile adaptation. */
|
|
309
729
|
const inject = ["slots"];
|
|
310
|
-
function controlLabel() {
|
|
311
|
-
return navigator.language.toLowerCase().startsWith("zh") ? "移动端" : "Mobile access";
|
|
312
|
-
}
|
|
313
730
|
//#endregion
|
|
314
731
|
exports.apply = apply;
|
|
315
732
|
exports.inject = inject;
|