niuma-ui 1.3.3 → 1.3.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/CHANGELOG.md +13 -0
- package/dist/components/RsLog.css +42 -39
- package/dist/components/RsLog.impl.js +13 -1
- package/dist/components/RsLog.js +1 -1
- package/dist/components/RsTerminal.css +23 -23
- package/dist/components/RsTerminal.d.ts +7 -0
- package/dist/components/RsTerminal.impl.js +35 -7
- package/dist/components/RsTerminal.js +1 -1
- package/dist/components/terminal-utils.d.ts +26 -0
- package/dist/components/terminal-utils.js +47 -2
- package/dist/locale/messages.js +2 -0
- package/dist/utils/rs-clipboard.d.ts +3 -1
- package/dist/utils/rs-clipboard.js +13 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.5] - 2026-09-12
|
|
10
|
+
|
|
11
|
+
### 修复
|
|
12
|
+
|
|
13
|
+
- `RsTerminal`:默认 `minimumContrastRatio=4.5`。浅色主题下 `ls --color` 软链接(黑底叠深色字)不再看不清;传 `1` 可关闭。
|
|
14
|
+
- `RsTerminal`:`fit` 按宿主 `clientWidth/Height` 纠正行列,避免 FitAddon 把 `height: 100%` 算成内容高、PTY 停在默认 80×24。画布仍高出一行再减行。全屏清屏 / 进备用屏才回视口底部。
|
|
15
|
+
|
|
16
|
+
## [1.3.4] - 2026-09-11
|
|
17
|
+
|
|
18
|
+
### 修复
|
|
19
|
+
|
|
20
|
+
- `RsLog` 在 HTTP(非安全上下文)下复制:不再先走会失败的 Clipboard API(`await` 会丢掉用户手势),改为同步 `execCommand`;级别/行号不可选中,避免复制出「输出」「信息」。失败时工具栏提示复制失败。
|
|
21
|
+
|
|
9
22
|
## [1.3.3] - 2026-09-09
|
|
10
23
|
|
|
11
24
|
### 修复
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
.rs-log[data-v-
|
|
2
|
+
.rs-log[data-v-0c4db684] {
|
|
3
3
|
display: flex;
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
min-height: 0;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
line-height: 1.45;
|
|
14
14
|
height: var(--rs-log-box-height, 280px);
|
|
15
15
|
}
|
|
16
|
-
.rs-log__chrome[data-v-
|
|
16
|
+
.rs-log__chrome[data-v-0c4db684] {
|
|
17
17
|
display: flex;
|
|
18
18
|
flex-wrap: wrap;
|
|
19
19
|
gap: 0.45rem;
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
border-bottom: 1px solid var(--rs-log-border, var(--rs-border));
|
|
23
23
|
background: color-mix(in srgb, var(--rs-log-bg, var(--rs-surface)) 88%, var(--rs-surface-hover));
|
|
24
24
|
}
|
|
25
|
-
.rs-log__search[data-v-
|
|
25
|
+
.rs-log__search[data-v-0c4db684] {
|
|
26
26
|
min-width: 10rem;
|
|
27
27
|
flex: 1 1 12rem;
|
|
28
28
|
}
|
|
29
|
-
.rs-log__copy[data-v-
|
|
29
|
+
.rs-log__copy[data-v-0c4db684] {
|
|
30
30
|
flex-shrink: 0;
|
|
31
31
|
margin-inline-start: auto;
|
|
32
32
|
}
|
|
33
|
-
.rs-log__filters[data-v-
|
|
33
|
+
.rs-log__filters[data-v-0c4db684] {
|
|
34
34
|
display: flex;
|
|
35
35
|
flex-wrap: wrap;
|
|
36
36
|
gap: 0.3rem;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
border: 0;
|
|
40
40
|
min-inline-size: 0;
|
|
41
41
|
}
|
|
42
|
-
.rs-log__chip[data-v-
|
|
42
|
+
.rs-log__chip[data-v-0c4db684] {
|
|
43
43
|
margin: 0;
|
|
44
44
|
padding: 0.1rem 0.4rem;
|
|
45
45
|
border: 1px solid var(--rs-log-border, var(--rs-border));
|
|
@@ -50,32 +50,32 @@
|
|
|
50
50
|
font-size: var(--rs-font-size-xs);
|
|
51
51
|
cursor: pointer;
|
|
52
52
|
}
|
|
53
|
-
.rs-log__chip--on[data-v-
|
|
53
|
+
.rs-log__chip--on[data-v-0c4db684] {
|
|
54
54
|
color: var(--rs-log-fg, var(--rs-text));
|
|
55
55
|
border-color: color-mix(in srgb, var(--rs-primary) 45%, var(--rs-log-border, var(--rs-border)));
|
|
56
56
|
background: color-mix(in srgb, var(--rs-primary) 14%, transparent);
|
|
57
57
|
}
|
|
58
|
-
.rs-log__viewport[data-v-
|
|
58
|
+
.rs-log__viewport[data-v-0c4db684] {
|
|
59
59
|
display: flex;
|
|
60
60
|
flex-direction: column;
|
|
61
61
|
flex: 1;
|
|
62
62
|
min-height: 0;
|
|
63
63
|
outline: none;
|
|
64
64
|
}
|
|
65
|
-
.rs-log__viewport[data-v-
|
|
65
|
+
.rs-log__viewport[data-v-0c4db684]:focus-visible {
|
|
66
66
|
box-shadow: inset 0 0 0 var(--rs-focus-ring-width, 2px) var(--rs-focus-border);
|
|
67
67
|
}
|
|
68
|
-
.rs-log__list[data-v-
|
|
69
|
-
.rs-log__plain[data-v-
|
|
68
|
+
.rs-log__list[data-v-0c4db684],
|
|
69
|
+
.rs-log__plain[data-v-0c4db684] {
|
|
70
70
|
flex: 1;
|
|
71
71
|
min-height: 0;
|
|
72
72
|
border: 0;
|
|
73
73
|
background: transparent;
|
|
74
74
|
}
|
|
75
|
-
.rs-log__plain[data-v-
|
|
75
|
+
.rs-log__plain[data-v-0c4db684] {
|
|
76
76
|
overflow: auto;
|
|
77
77
|
}
|
|
78
|
-
.rs-log__row[data-v-
|
|
78
|
+
.rs-log__row[data-v-0c4db684] {
|
|
79
79
|
display: flex;
|
|
80
80
|
align-items: flex-start;
|
|
81
81
|
gap: 0.55rem;
|
|
@@ -84,13 +84,13 @@
|
|
|
84
84
|
white-space: nowrap;
|
|
85
85
|
color: var(--rs-log-row-fg, var(--rs-log-fg));
|
|
86
86
|
}
|
|
87
|
-
.rs-log:not(.rs-log--wrap) .rs-log__list .rs-log__row[data-v-
|
|
87
|
+
.rs-log:not(.rs-log--wrap) .rs-log__list .rs-log__row[data-v-0c4db684] {
|
|
88
88
|
min-height: 100%;
|
|
89
89
|
}
|
|
90
|
-
.rs-log--wrap .rs-log__plain[data-v-
|
|
90
|
+
.rs-log--wrap .rs-log__plain[data-v-0c4db684] {
|
|
91
91
|
display: block;
|
|
92
92
|
}
|
|
93
|
-
.rs-log--wrap .rs-log__row[data-v-
|
|
93
|
+
.rs-log--wrap .rs-log__row[data-v-0c4db684] {
|
|
94
94
|
height: auto;
|
|
95
95
|
max-height: none;
|
|
96
96
|
min-height: 0;
|
|
@@ -99,14 +99,14 @@
|
|
|
99
99
|
word-break: break-word;
|
|
100
100
|
padding-block: 0.25rem;
|
|
101
101
|
}
|
|
102
|
-
.rs-log--zebra .rs-log__row--odd[data-v-
|
|
102
|
+
.rs-log--zebra .rs-log__row--odd[data-v-0c4db684] {
|
|
103
103
|
background: var(--rs-log-stripe, transparent);
|
|
104
104
|
}
|
|
105
|
-
.rs-log__row--active[data-v-
|
|
105
|
+
.rs-log__row--active[data-v-0c4db684] {
|
|
106
106
|
background: color-mix(in srgb, var(--rs-primary) 12%, var(--rs-log-bg, transparent));
|
|
107
107
|
box-shadow: inset 2px 0 0 var(--rs-primary);
|
|
108
108
|
}
|
|
109
|
-
.rs-log__marker[data-v-
|
|
109
|
+
.rs-log__marker[data-v-0c4db684] {
|
|
110
110
|
flex-shrink: 0;
|
|
111
111
|
width: 0.2rem;
|
|
112
112
|
align-self: stretch;
|
|
@@ -114,44 +114,47 @@
|
|
|
114
114
|
margin-block: 0.2rem;
|
|
115
115
|
border-radius: 99px;
|
|
116
116
|
background: currentColor;
|
|
117
|
+
user-select: none;
|
|
117
118
|
}
|
|
118
|
-
.rs-log__no[data-v-
|
|
119
|
-
.rs-log__time[data-v-
|
|
119
|
+
.rs-log__no[data-v-0c4db684],
|
|
120
|
+
.rs-log__time[data-v-0c4db684] {
|
|
120
121
|
flex-shrink: 0;
|
|
121
122
|
color: var(--rs-log-muted, var(--rs-text-muted));
|
|
122
123
|
font-variant-numeric: tabular-nums;
|
|
123
124
|
unicode-bidi: isolate;
|
|
125
|
+
user-select: none;
|
|
124
126
|
}
|
|
125
|
-
.rs-log__level[data-v-
|
|
127
|
+
.rs-log__level[data-v-0c4db684] {
|
|
126
128
|
flex-shrink: 0;
|
|
127
129
|
min-width: 3.6rem;
|
|
128
130
|
font-weight: var(--rs-font-weight-semibold);
|
|
129
131
|
letter-spacing: 0.02em;
|
|
130
132
|
unicode-bidi: isolate;
|
|
133
|
+
user-select: none;
|
|
131
134
|
}
|
|
132
|
-
.rs-log__text[data-v-
|
|
135
|
+
.rs-log__text[data-v-0c4db684] {
|
|
133
136
|
min-width: 0;
|
|
134
137
|
flex: 1;
|
|
135
138
|
font-weight: inherit;
|
|
136
139
|
unicode-bidi: plaintext;
|
|
137
140
|
user-select: text;
|
|
138
141
|
}
|
|
139
|
-
.rs-log--wrap .rs-log__text[data-v-
|
|
142
|
+
.rs-log--wrap .rs-log__text[data-v-0c4db684] {
|
|
140
143
|
overflow: visible;
|
|
141
144
|
height: auto;
|
|
142
145
|
min-height: 0;
|
|
143
146
|
}
|
|
144
|
-
.rs-log:not(.rs-log--wrap) .rs-log__text[data-v-
|
|
147
|
+
.rs-log:not(.rs-log--wrap) .rs-log__text[data-v-0c4db684] {
|
|
145
148
|
overflow: hidden;
|
|
146
149
|
text-overflow: ellipsis;
|
|
147
150
|
}
|
|
148
|
-
.rs-log__hit[data-v-
|
|
151
|
+
.rs-log__hit[data-v-0c4db684] {
|
|
149
152
|
padding: 0 0.1em;
|
|
150
153
|
background: var(--rs-log-hit-bg);
|
|
151
154
|
color: var(--rs-log-hit-fg);
|
|
152
155
|
font: inherit;
|
|
153
156
|
}
|
|
154
|
-
.rs-log__sr-only[data-v-
|
|
157
|
+
.rs-log__sr-only[data-v-0c4db684] {
|
|
155
158
|
position: absolute;
|
|
156
159
|
width: 1px;
|
|
157
160
|
height: 1px;
|
|
@@ -162,47 +165,47 @@
|
|
|
162
165
|
white-space: nowrap;
|
|
163
166
|
border: 0;
|
|
164
167
|
}
|
|
165
|
-
.rs-log__row--plain[data-v-
|
|
168
|
+
.rs-log__row--plain[data-v-0c4db684] {
|
|
166
169
|
--rs-log-row-fg: var(--rs-log-fg);
|
|
167
170
|
font-weight: var(--rs-font-weight-regular);
|
|
168
171
|
}
|
|
169
|
-
.rs-log__row--trace[data-v-
|
|
172
|
+
.rs-log__row--trace[data-v-0c4db684] {
|
|
170
173
|
--rs-log-row-fg: var(--rs-log-trace);
|
|
171
174
|
font-weight: var(--rs-font-weight-regular);
|
|
172
175
|
}
|
|
173
|
-
.rs-log__row--debug[data-v-
|
|
176
|
+
.rs-log__row--debug[data-v-0c4db684] {
|
|
174
177
|
--rs-log-row-fg: var(--rs-log-debug);
|
|
175
178
|
font-weight: var(--rs-font-weight-regular);
|
|
176
179
|
}
|
|
177
|
-
.rs-log__row--info[data-v-
|
|
178
|
-
.rs-log__row--notice[data-v-
|
|
180
|
+
.rs-log__row--info[data-v-0c4db684],
|
|
181
|
+
.rs-log__row--notice[data-v-0c4db684] {
|
|
179
182
|
--rs-log-row-fg: var(--rs-log-info);
|
|
180
183
|
font-weight: var(--rs-font-weight-medium);
|
|
181
184
|
}
|
|
182
|
-
.rs-log__row--notice[data-v-
|
|
185
|
+
.rs-log__row--notice[data-v-0c4db684] {
|
|
183
186
|
--rs-log-row-fg: var(--rs-log-notice);
|
|
184
187
|
}
|
|
185
|
-
.rs-log__row--success[data-v-
|
|
188
|
+
.rs-log__row--success[data-v-0c4db684] {
|
|
186
189
|
--rs-log-row-fg: var(--rs-log-success);
|
|
187
190
|
font-weight: var(--rs-font-weight-medium);
|
|
188
191
|
}
|
|
189
|
-
.rs-log__row--warn[data-v-
|
|
192
|
+
.rs-log__row--warn[data-v-0c4db684] {
|
|
190
193
|
--rs-log-row-fg: var(--rs-log-warn);
|
|
191
194
|
font-weight: var(--rs-font-weight-semibold);
|
|
192
195
|
}
|
|
193
|
-
.rs-log__row--error[data-v-
|
|
196
|
+
.rs-log__row--error[data-v-0c4db684] {
|
|
194
197
|
--rs-log-row-fg: var(--rs-log-error);
|
|
195
198
|
font-weight: var(--rs-font-weight-bold);
|
|
196
199
|
}
|
|
197
|
-
.rs-log__row--fatal[data-v-
|
|
200
|
+
.rs-log__row--fatal[data-v-0c4db684] {
|
|
198
201
|
--rs-log-row-fg: var(--rs-log-fatal);
|
|
199
202
|
font-weight: var(--rs-font-weight-bold);
|
|
200
203
|
}
|
|
201
204
|
@media (forced-colors: active) {
|
|
202
|
-
.rs-log__marker[data-v-
|
|
205
|
+
.rs-log__marker[data-v-0c4db684] {
|
|
203
206
|
background: ButtonText;
|
|
204
207
|
}
|
|
205
|
-
.rs-log__row--active[data-v-
|
|
208
|
+
.rs-log__row--active[data-v-0c4db684] {
|
|
206
209
|
outline: 2px solid Highlight;
|
|
207
210
|
outline-offset: -2px;
|
|
208
211
|
}
|
|
@@ -209,8 +209,10 @@ var RsLog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
|
|
|
209
209
|
const emptyLabel = computed(() => props.emptyText || t("log.empty", "No log output"));
|
|
210
210
|
const regionLabel = computed(() => props.ariaLabel || t("log.label", "Log"));
|
|
211
211
|
const hasChrome = computed(() => props.showSearch || props.showCopy || props.showFilter || Boolean(slots.toolbar));
|
|
212
|
+
const copyFailed = ref(false);
|
|
212
213
|
const copyTip = computed(() => {
|
|
213
214
|
if (copied.value) return t("log.copied", "Copied");
|
|
215
|
+
if (copyFailed.value) return t("log.copyFailed", "Copy failed");
|
|
214
216
|
return t("log.copy", "Copy");
|
|
215
217
|
});
|
|
216
218
|
const rootStyle = computed(() => ({
|
|
@@ -292,12 +294,22 @@ var RsLog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
|
|
|
292
294
|
}
|
|
293
295
|
function markCopied() {
|
|
294
296
|
copied.value = true;
|
|
297
|
+
copyFailed.value = false;
|
|
295
298
|
if (copiedTimer) window.clearTimeout(copiedTimer);
|
|
296
299
|
copiedTimer = window.setTimeout(() => {
|
|
297
300
|
copied.value = false;
|
|
298
301
|
copiedTimer = 0;
|
|
299
302
|
}, 1500);
|
|
300
303
|
}
|
|
304
|
+
function markCopyFailed() {
|
|
305
|
+
copyFailed.value = true;
|
|
306
|
+
copied.value = false;
|
|
307
|
+
if (copiedTimer) window.clearTimeout(copiedTimer);
|
|
308
|
+
copiedTimer = window.setTimeout(() => {
|
|
309
|
+
copyFailed.value = false;
|
|
310
|
+
copiedTimer = 0;
|
|
311
|
+
}, 2500);
|
|
312
|
+
}
|
|
301
313
|
async function writeCopy(text, source) {
|
|
302
314
|
if (!text) return false;
|
|
303
315
|
const ok = await copyTextToClipboard(text);
|
|
@@ -307,7 +319,7 @@ var RsLog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
|
|
|
307
319
|
text,
|
|
308
320
|
source
|
|
309
321
|
});
|
|
310
|
-
}
|
|
322
|
+
} else markCopyFailed();
|
|
311
323
|
return ok;
|
|
312
324
|
}
|
|
313
325
|
async function copy() {
|
package/dist/components/RsLog.js
CHANGED
|
@@ -3,6 +3,6 @@ import _plugin_vue_export_helper_default from "../_virtual/_plugin-vue_export-he
|
|
|
3
3
|
import RsLog_vue_vue_type_script_setup_true_lang_default from "./RsLog.impl.js";
|
|
4
4
|
|
|
5
5
|
//#region src/components/RsLog.vue
|
|
6
|
-
var RsLog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RsLog_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
6
|
+
var RsLog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RsLog_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-0c4db684"]]);
|
|
7
7
|
//#endregion
|
|
8
8
|
export { RsLog_default as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
.rs-terminal[data-v-
|
|
2
|
+
.rs-terminal[data-v-db06bdc1] {
|
|
3
3
|
position: relative;
|
|
4
4
|
height: 100%;
|
|
5
5
|
min-height: 0;
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
0 1px 3px color-mix(in srgb, #000 32%, transparent);
|
|
17
17
|
overflow: hidden;
|
|
18
18
|
}
|
|
19
|
-
[data-rs-theme='light'] .rs-terminal[data-v-
|
|
19
|
+
[data-rs-theme='light'] .rs-terminal[data-v-db06bdc1] {
|
|
20
20
|
box-shadow:
|
|
21
21
|
inset 0 1px 0 color-mix(in srgb, #fff 80%, transparent),
|
|
22
22
|
inset 0 0 0 1px color-mix(in srgb, #000 4%, transparent),
|
|
23
23
|
0 1px 4px color-mix(in srgb, #000 8%, transparent);
|
|
24
24
|
}
|
|
25
|
-
.rs-terminal__host[data-v-
|
|
25
|
+
.rs-terminal__host[data-v-db06bdc1] {
|
|
26
26
|
position: relative;
|
|
27
27
|
width: 100%;
|
|
28
28
|
height: 100%;
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
/* FitAddon 取整余量露底时,与单元格同色(勿用 shell 灰底) */
|
|
31
31
|
background: var(--rs-terminal-bg);
|
|
32
32
|
}
|
|
33
|
-
.rs-terminal__loading[data-v-
|
|
34
|
-
.rs-terminal__overlay[data-v-
|
|
33
|
+
.rs-terminal__loading[data-v-db06bdc1],
|
|
34
|
+
.rs-terminal__overlay[data-v-db06bdc1] {
|
|
35
35
|
position: absolute;
|
|
36
36
|
inset: 0;
|
|
37
37
|
display: flex;
|
|
38
38
|
align-items: center;
|
|
39
39
|
justify-content: center;
|
|
40
40
|
}
|
|
41
|
-
.rs-terminal__overlay[data-v-
|
|
41
|
+
.rs-terminal__overlay[data-v-db06bdc1] {
|
|
42
42
|
flex-direction: column;
|
|
43
43
|
gap: var(--rs-space-sm);
|
|
44
44
|
padding: var(--rs-space-lg);
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
font-weight: var(--rs-font-weight-regular);
|
|
52
52
|
line-height: var(--rs-line-height-normal);
|
|
53
53
|
}
|
|
54
|
-
.rs-terminal__overlay--interactive[data-v-
|
|
54
|
+
.rs-terminal__overlay--interactive[data-v-db06bdc1] {
|
|
55
55
|
pointer-events: auto;
|
|
56
56
|
}
|
|
57
|
-
.rs-terminal__overlay-action[data-v-
|
|
57
|
+
.rs-terminal__overlay-action[data-v-db06bdc1] {
|
|
58
58
|
pointer-events: auto;
|
|
59
59
|
}
|
|
60
|
-
.rs-terminal__search[data-v-
|
|
60
|
+
.rs-terminal__search[data-v-db06bdc1] {
|
|
61
61
|
position: absolute;
|
|
62
62
|
top: var(--rs-space-xs);
|
|
63
63
|
right: var(--rs-space-xs);
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
border-radius: var(--rs-radius-sm);
|
|
71
71
|
background: color-mix(in srgb, var(--rs-terminal-bg) 92%, transparent);
|
|
72
72
|
}
|
|
73
|
-
.rs-terminal__search-input[data-v-
|
|
73
|
+
.rs-terminal__search-input[data-v-db06bdc1] {
|
|
74
74
|
width: 12rem;
|
|
75
75
|
padding: 0.2rem 0.4rem;
|
|
76
76
|
border: 1px solid var(--rs-terminal-border);
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
font-size: var(--rs-font-size-xs);
|
|
82
82
|
outline: none;
|
|
83
83
|
}
|
|
84
|
-
.rs-terminal__search-btn[data-v-
|
|
84
|
+
.rs-terminal__search-btn[data-v-db06bdc1] {
|
|
85
85
|
padding: 0.2rem 0.4rem;
|
|
86
86
|
border: 1px solid var(--rs-terminal-border);
|
|
87
87
|
border-radius: var(--rs-radius-sm);
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
* DOM 渲染器于是按 `cellWidth - 测量值` 给每个字符补正字距,行盒却仍是 cols × cellWidth,
|
|
99
99
|
* 逐列累积后整行溢出十几到几十像素,被行盒的 overflow:hidden 切掉行尾几个字符。
|
|
100
100
|
*/
|
|
101
|
-
.rs-terminal[data-v-
|
|
101
|
+
.rs-terminal[data-v-db06bdc1] .xterm {
|
|
102
102
|
width: 100%;
|
|
103
103
|
height: 100%;
|
|
104
104
|
line-height: normal;
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
* xterm 6 官方仍给空的 .xterm-viewport 写 overflow-y:scroll + 背景 #000。
|
|
110
110
|
* 真正滚动的是 .xterm-scrollable-element;空层滚动条会挡住最后一列。
|
|
111
111
|
*/
|
|
112
|
-
.rs-terminal[data-v-
|
|
112
|
+
.rs-terminal[data-v-db06bdc1] .xterm .xterm-viewport {
|
|
113
113
|
overflow: hidden;
|
|
114
114
|
background-color: transparent;
|
|
115
115
|
}
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
* 的圆角胶囊不一致。轨道宽度由 overviewRuler.width 决定,改它会给每个终端实例多建一块
|
|
120
120
|
* overview ruler 画布,因此只重绘滑块:热区仍是 14px,靠透明边框内缩出 10px 可见胶囊。
|
|
121
121
|
*/
|
|
122
|
-
.rs-terminal[data-v-
|
|
122
|
+
.rs-terminal[data-v-db06bdc1] .xterm-scrollable-element > .scrollbar > .slider {
|
|
123
123
|
box-sizing: border-box;
|
|
124
124
|
border: var(--rs-scrollbar-padding, 2px) solid transparent;
|
|
125
125
|
border-radius: var(--rs-radius-full);
|
|
@@ -127,18 +127,18 @@
|
|
|
127
127
|
background-clip: padding-box;
|
|
128
128
|
transition: background-color var(--rs-transition-fast);
|
|
129
129
|
}
|
|
130
|
-
.rs-terminal[data-v-
|
|
130
|
+
.rs-terminal[data-v-db06bdc1] .xterm-scrollable-element > .scrollbar > .slider:hover {
|
|
131
131
|
background-color: color-mix(in srgb, var(--rs-primary) 40%, var(--rs-muted));
|
|
132
132
|
}
|
|
133
|
-
.rs-terminal[data-v-
|
|
133
|
+
.rs-terminal[data-v-db06bdc1] .xterm-scrollable-element > .scrollbar > .slider.active {
|
|
134
134
|
background-color: color-mix(in srgb, var(--rs-primary) 55%, var(--rs-muted));
|
|
135
135
|
}
|
|
136
|
-
.rs-terminal--zebra[data-v-
|
|
137
|
-
.rs-terminal--zebra[data-v-
|
|
138
|
-
.rs-terminal--zebra[data-v-
|
|
136
|
+
.rs-terminal--zebra[data-v-db06bdc1] .xterm-rows,
|
|
137
|
+
.rs-terminal--zebra[data-v-db06bdc1] .xterm-rows > div,
|
|
138
|
+
.rs-terminal--zebra[data-v-db06bdc1] .xterm-row {
|
|
139
139
|
background-color: transparent !important;
|
|
140
140
|
}
|
|
141
|
-
.rs-terminal--zebra[data-v-
|
|
141
|
+
.rs-terminal--zebra[data-v-db06bdc1] .xterm-scrollable-element {
|
|
142
142
|
background-color: transparent !important;
|
|
143
143
|
background-image: repeating-linear-gradient(
|
|
144
144
|
to bottom,
|
|
@@ -149,12 +149,12 @@
|
|
|
149
149
|
);
|
|
150
150
|
background-attachment: local;
|
|
151
151
|
}
|
|
152
|
-
.rs-terminal--zebra[data-v-
|
|
152
|
+
.rs-terminal--zebra[data-v-db06bdc1] .xterm-screen {
|
|
153
153
|
background-color: transparent !important;
|
|
154
154
|
}
|
|
155
|
-
.rs-terminal[data-v-
|
|
155
|
+
.rs-terminal[data-v-db06bdc1] .xterm-fg-257 {
|
|
156
156
|
color: var(--rs-terminal-bg) !important;
|
|
157
157
|
}
|
|
158
|
-
.rs-terminal[data-v-
|
|
158
|
+
.rs-terminal[data-v-db06bdc1] .xterm-bg-257 {
|
|
159
159
|
background-color: var(--rs-terminal-fg) !important;
|
|
160
160
|
}
|
|
@@ -46,6 +46,12 @@ type __VLS_Props = {
|
|
|
46
46
|
copyOnSelect?: boolean;
|
|
47
47
|
/** 启用 Ctrl/⌘+F 终端内搜索 */
|
|
48
48
|
searchEnabled?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 单元格前景相对背景的最低对比度(xterm `minimumContrastRatio`)。
|
|
51
|
+
* `ls --color` 软链接常用黑底;浅色主题把青/蓝调深后会看不清,4.5 对齐 WCAG AA。
|
|
52
|
+
* 传 1 关闭。
|
|
53
|
+
*/
|
|
54
|
+
minimumContrastRatio?: number;
|
|
49
55
|
};
|
|
50
56
|
declare var __VLS_15: {};
|
|
51
57
|
type __VLS_Slots = {} & {
|
|
@@ -90,6 +96,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, RsTerminalE
|
|
|
90
96
|
snapViewportOnTuiWrite: boolean;
|
|
91
97
|
copyOnSelect: boolean;
|
|
92
98
|
searchEnabled: boolean;
|
|
99
|
+
minimumContrastRatio: number;
|
|
93
100
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
94
101
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
95
102
|
declare const _default: typeof __VLS_export;
|
|
@@ -3,7 +3,7 @@ import RsLoading_default from "./RsLoading.js";
|
|
|
3
3
|
import RsContextMenu_default from "./RsContextMenu.js";
|
|
4
4
|
import { beginClipboardPrefetch, copyTextToClipboard, readClipboardText } from "../utils/rs-clipboard.js";
|
|
5
5
|
import { readTerminalFontFamily, readTerminalFontSizePx, readTerminalFontWeight, readTerminalFontWeightBold } from "../theme/css-token.js";
|
|
6
|
-
import {
|
|
6
|
+
import { mergeTerminalTheme, needsPtyWriteViewportPrep, prepareTerminalForPtyWrite, proposeTerminalGeometry, readXtermCssCellSize, resolveTerminalTheme, terminalShortcutLabel } from "./terminal-utils.js";
|
|
7
7
|
import { attachWheelScrollGuard, isAlternateTerminalBuffer } from "./terminal-wheel.js";
|
|
8
8
|
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, mergeProps, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderSlot, toDisplayString, unref, vModelText, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
9
9
|
import { FitAddon } from "@xterm/addon-fit";
|
|
@@ -85,7 +85,8 @@ var RsTerminal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
85
85
|
searchEnabled: {
|
|
86
86
|
type: Boolean,
|
|
87
87
|
default: false
|
|
88
|
-
}
|
|
88
|
+
},
|
|
89
|
+
minimumContrastRatio: { default: 4.5 }
|
|
89
90
|
},
|
|
90
91
|
emits: [
|
|
91
92
|
"ready",
|
|
@@ -268,14 +269,37 @@ var RsTerminal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
268
269
|
if (!fitAddon || !terminal) return;
|
|
269
270
|
try {
|
|
270
271
|
fitAddon.fit();
|
|
271
|
-
} catch {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
272
|
+
} catch {}
|
|
274
273
|
didInitialFit = true;
|
|
275
|
-
|
|
274
|
+
applyHostBoxGeometry();
|
|
275
|
+
trimOverflowRow();
|
|
276
276
|
syncZebraRowStepFromDom();
|
|
277
|
+
emitResizeIfChanged();
|
|
277
278
|
syncResolvedFontSize();
|
|
278
279
|
}
|
|
280
|
+
/** 以 host client 盒为准,纠正 FitAddon 把 100% 高度 parseInt 成内容高(默认 24 行)的情况。 */
|
|
281
|
+
function applyHostBoxGeometry() {
|
|
282
|
+
if (!terminal || !hostEl.value) return;
|
|
283
|
+
const cell = readXtermCssCellSize(terminal);
|
|
284
|
+
if (!cell) return;
|
|
285
|
+
const proposed = proposeTerminalGeometry(hostEl.value.clientWidth, hostEl.value.clientHeight, cell.width, cell.height);
|
|
286
|
+
if (!proposed) return;
|
|
287
|
+
const cols = Math.max(proposed.cols, 20);
|
|
288
|
+
const rows = Math.max(proposed.rows, 5);
|
|
289
|
+
if (cols !== terminal.cols || rows !== terminal.rows) terminal.resize(cols, rows);
|
|
290
|
+
}
|
|
291
|
+
/** 画布比宿主高出超过一行时再减行,避免裁掉最后一行。 */
|
|
292
|
+
function trimOverflowRow() {
|
|
293
|
+
if (!terminal || !hostEl.value) return;
|
|
294
|
+
const screen = hostEl.value.querySelector(".xterm-screen");
|
|
295
|
+
if (!screen) return;
|
|
296
|
+
const overflow = screen.offsetHeight - hostEl.value.clientHeight;
|
|
297
|
+
if (overflow <= 1) return;
|
|
298
|
+
const cell = readXtermCssCellSize(terminal);
|
|
299
|
+
const cut = cell ? Math.max(1, Math.ceil(overflow / cell.height)) : 1;
|
|
300
|
+
const nextRows = Math.max(5, terminal.rows - cut);
|
|
301
|
+
if (nextRows < terminal.rows) terminal.resize(terminal.cols, nextRows);
|
|
302
|
+
}
|
|
279
303
|
function scheduleFit() {
|
|
280
304
|
if (fitRaf) return;
|
|
281
305
|
fitRaf = requestAnimationFrame(() => {
|
|
@@ -286,7 +310,7 @@ var RsTerminal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
286
310
|
function write(data) {
|
|
287
311
|
const term = terminal;
|
|
288
312
|
if (!term) return;
|
|
289
|
-
if (props.snapViewportOnTuiWrite &&
|
|
313
|
+
if (props.snapViewportOnTuiWrite && needsPtyWriteViewportPrep(data, term)) prepareTerminalForPtyWrite(term, data);
|
|
290
314
|
term.write(data);
|
|
291
315
|
}
|
|
292
316
|
function clear() {
|
|
@@ -488,6 +512,9 @@ var RsTerminal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
488
512
|
});
|
|
489
513
|
watch(() => [props.themeMode, props.theme], refreshResolvedTheme, { deep: true });
|
|
490
514
|
watch(() => props.wheelScrollModifier, attachWheelGuard);
|
|
515
|
+
watch(() => props.minimumContrastRatio, (value) => {
|
|
516
|
+
if (terminal) terminal.options.minimumContrastRatio = value;
|
|
517
|
+
});
|
|
491
518
|
onMounted(async () => {
|
|
492
519
|
if (!hostEl.value) return;
|
|
493
520
|
refreshResolvedTheme();
|
|
@@ -501,6 +528,7 @@ var RsTerminal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
501
528
|
letterSpacing: 0,
|
|
502
529
|
allowTransparency: resolveAllowTransparency(),
|
|
503
530
|
drawBoldTextInBrightColors: true,
|
|
531
|
+
minimumContrastRatio: props.minimumContrastRatio,
|
|
504
532
|
scrollback: props.scrollback,
|
|
505
533
|
convertEol: props.convertEol,
|
|
506
534
|
rightClickSelectsWord: props.rightClickSelectsWord,
|
|
@@ -3,6 +3,6 @@ import _plugin_vue_export_helper_default from "../_virtual/_plugin-vue_export-he
|
|
|
3
3
|
import RsTerminal_vue_vue_type_script_setup_true_lang_default from "./RsTerminal.impl.js";
|
|
4
4
|
|
|
5
5
|
//#region src/components/RsTerminal.vue
|
|
6
|
-
var RsTerminal_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RsTerminal_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
6
|
+
var RsTerminal_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RsTerminal_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-db06bdc1"]]);
|
|
7
7
|
//#endregion
|
|
8
8
|
export { RsTerminal_default as default };
|
|
@@ -40,6 +40,27 @@ export declare function readTerminalRowStripeFromCss(mode: RsResolvedTerminalThe
|
|
|
40
40
|
export declare function containsEscapeSequence(data: string): boolean;
|
|
41
41
|
/** 检测 ncurses/vim/top 等全屏刷新常用的光标定位与清屏序列 */
|
|
42
42
|
export declare function containsTuiRefreshSequence(data: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 按宿主像素高度与实测行高计算能完整放下的行数。
|
|
45
|
+
* FitAddon 用理论 cellHeight,亚像素取整后行盒往往更高,多报的行会被 overflow 裁掉
|
|
46
|
+
*(top 按 1 展开几十上百核时,最后几行 CPU 就像被高度挡住)。
|
|
47
|
+
*/
|
|
48
|
+
export declare function computeFittedTerminalRows(hostHeight: number, rowHeight: number, minRows?: number): number | null;
|
|
49
|
+
/** 读取 xterm 当前 CSS 单元格尺寸(与 FitAddon 同一套私有 dimensions)。 */
|
|
50
|
+
export declare function readXtermCssCellSize(terminal: Terminal): {
|
|
51
|
+
width: number;
|
|
52
|
+
height: number;
|
|
53
|
+
} | null;
|
|
54
|
+
/**
|
|
55
|
+
* 用宿主 client 宽高算行列,避开 FitAddon 对 computed `height: 100%` 走 parseInt 的偏差。
|
|
56
|
+
* 偏差时 PTY 会停在默认 80×24,`top` 按 1 就只画二十来行 CPU。
|
|
57
|
+
*/
|
|
58
|
+
export declare function proposeTerminalGeometry(hostWidth: number, hostHeight: number, cellWidth: number, cellHeight: number, scrollbarWidth?: number): {
|
|
59
|
+
cols: number;
|
|
60
|
+
rows: number;
|
|
61
|
+
} | null;
|
|
62
|
+
/** 进入备用屏(smcup),top/vim 全屏时常走这条 */
|
|
63
|
+
export declare function containsAltScreenEnter(data: string): boolean;
|
|
43
64
|
/** 全屏清屏(top/vim 新一轮绘制常见) */
|
|
44
65
|
export declare function containsFullScreenClear(data: string): boolean;
|
|
45
66
|
/** 数据块以光标归位首行开头(top 每轮刷新常见) */
|
|
@@ -50,6 +71,11 @@ export declare function startsWithCursorHome(data: string): boolean;
|
|
|
50
71
|
*/
|
|
51
72
|
export declare function resetLocalTerminalModes(terminal: Terminal): void;
|
|
52
73
|
export declare function snapTerminalViewportToBottom(terminal: Terminal): void;
|
|
74
|
+
/**
|
|
75
|
+
* 是否要在写入前校正视口。
|
|
76
|
+
* 只认清屏 / 进备用屏 / 已在备用屏且视口不在底部;普通 `\x1b[H` 不碰,以免把用户翻的 scrollback 拽回去。
|
|
77
|
+
*/
|
|
78
|
+
export declare function needsPtyWriteViewportPrep(data: string, terminal: Terminal): boolean;
|
|
53
79
|
/** 处理 PTY 写入前的视口/模式准备(不移动写入光标,避免输入错位) */
|
|
54
80
|
export declare function prepareTerminalForPtyWrite(terminal: Terminal, data: string): void;
|
|
55
81
|
export declare function isMacPlatform(): boolean;
|
|
@@ -145,6 +145,32 @@ function containsTuiRefreshSequence(data) {
|
|
|
145
145
|
return TUI_REFRESH_PATTERN.test(data);
|
|
146
146
|
}
|
|
147
147
|
var FULL_SCREEN_CLEAR_PATTERN = /\x1b\[[0-9;]*2J|\x1b\[[0-9;]*3J/;
|
|
148
|
+
var ALT_SCREEN_ENTER_PATTERN = /\x1b\[\?1049h|\x1b\[\?47h|\x1b\[1049h/;
|
|
149
|
+
var DEFAULT_TERMINAL_SCROLLBAR_PX = 14;
|
|
150
|
+
/** 读取 xterm 当前 CSS 单元格尺寸(与 FitAddon 同一套私有 dimensions)。 */
|
|
151
|
+
function readXtermCssCellSize(terminal) {
|
|
152
|
+
const cell = terminal._core?._renderService?.dimensions?.css?.cell;
|
|
153
|
+
if (!cell || cell.width < 1 || cell.height < 1) return null;
|
|
154
|
+
return {
|
|
155
|
+
width: cell.width,
|
|
156
|
+
height: cell.height
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 用宿主 client 宽高算行列,避开 FitAddon 对 computed `height: 100%` 走 parseInt 的偏差。
|
|
161
|
+
* 偏差时 PTY 会停在默认 80×24,`top` 按 1 就只画二十来行 CPU。
|
|
162
|
+
*/
|
|
163
|
+
function proposeTerminalGeometry(hostWidth, hostHeight, cellWidth, cellHeight, scrollbarWidth = DEFAULT_TERMINAL_SCROLLBAR_PX) {
|
|
164
|
+
if (hostWidth <= 0 || hostHeight <= 0 || cellWidth < 1 || cellHeight < 1) return null;
|
|
165
|
+
return {
|
|
166
|
+
cols: Math.max(2, Math.floor((hostWidth - scrollbarWidth) / cellWidth)),
|
|
167
|
+
rows: Math.max(1, Math.floor(hostHeight / cellHeight))
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/** 进入备用屏(smcup),top/vim 全屏时常走这条 */
|
|
171
|
+
function containsAltScreenEnter(data) {
|
|
172
|
+
return ALT_SCREEN_ENTER_PATTERN.test(data);
|
|
173
|
+
}
|
|
148
174
|
/** 全屏清屏(top/vim 新一轮绘制常见) */
|
|
149
175
|
function containsFullScreenClear(data) {
|
|
150
176
|
return FULL_SCREEN_CLEAR_PATTERN.test(data);
|
|
@@ -159,10 +185,24 @@ function resetLocalTerminalModes(terminal) {
|
|
|
159
185
|
function snapTerminalViewportToBottom(terminal) {
|
|
160
186
|
terminal.scrollToBottom();
|
|
161
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* 是否要在写入前校正视口。
|
|
190
|
+
* 只认清屏 / 进备用屏 / 已在备用屏且视口不在底部;普通 `\x1b[H` 不碰,以免把用户翻的 scrollback 拽回去。
|
|
191
|
+
*/
|
|
192
|
+
function needsPtyWriteViewportPrep(data, terminal) {
|
|
193
|
+
if (containsFullScreenClear(data) || containsAltScreenEnter(data)) return true;
|
|
194
|
+
if (terminal.buffer.active.type !== "alternate") return false;
|
|
195
|
+
return containsTuiRefreshSequence(data) && terminal.buffer.active.viewportY < terminal.buffer.active.baseY;
|
|
196
|
+
}
|
|
162
197
|
/** 处理 PTY 写入前的视口/模式准备(不移动写入光标,避免输入错位) */
|
|
163
198
|
function prepareTerminalForPtyWrite(terminal, data) {
|
|
164
199
|
if (containsFullScreenClear(data)) {
|
|
165
200
|
resetLocalTerminalModes(terminal);
|
|
201
|
+
snapTerminalViewportToBottom(terminal);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (containsAltScreenEnter(data)) {
|
|
205
|
+
snapTerminalViewportToBottom(terminal);
|
|
166
206
|
return;
|
|
167
207
|
}
|
|
168
208
|
const buffer = terminal.buffer.active;
|
|
@@ -203,8 +243,13 @@ function buildAnsiColorDemo() {
|
|
|
203
243
|
"\x1B[1mStyles\x1B[0m \x1B[1mbold\x1B[0m \x1B[3mitalic\x1B[0m \x1B[4munderline\x1B[0m \x1B[9mstrikethrough\x1B[0m",
|
|
204
244
|
"",
|
|
205
245
|
"\x1B[38;5;196m256-color\x1B[0m sample:",
|
|
206
|
-
gradient
|
|
246
|
+
gradient,
|
|
247
|
+
"",
|
|
248
|
+
"\x1B[1mls --color symlink\x1B[0m",
|
|
249
|
+
` 23 Apr 1 2024 \x1b[01;36mbusiness-logs -> /wmsdata/business-logs/\x1b[0m`,
|
|
250
|
+
` 23 Apr 1 2024 \x1b[01;36;40mbusiness-logs -> /wmsdata/business-logs/\x1b[0m`,
|
|
251
|
+
` 23 Apr 1 2024 \x1b[40;31;01mbusiness-logs -> /wmsdata/business-logs/\x1b[0m`
|
|
207
252
|
].join("\r\n")}\r\n`;
|
|
208
253
|
}
|
|
209
254
|
//#endregion
|
|
210
|
-
export { buildAnsiColorDemo, containsEscapeSequence, containsFullScreenClear, containsTuiRefreshSequence, getTerminalThemePalette, isMacPlatform, mergeTerminalTheme, normalizeTerminalHexColor, prepareTerminalForPtyWrite, readTerminalThemeFromCss, resetLocalTerminalModes, resolveCssColorForXterm, resolveTerminalTheme, snapTerminalViewportToBottom, terminalShortcutLabel };
|
|
255
|
+
export { buildAnsiColorDemo, containsAltScreenEnter, containsEscapeSequence, containsFullScreenClear, containsTuiRefreshSequence, getTerminalThemePalette, isMacPlatform, mergeTerminalTheme, needsPtyWriteViewportPrep, normalizeTerminalHexColor, prepareTerminalForPtyWrite, proposeTerminalGeometry, readTerminalThemeFromCss, readXtermCssCellSize, resetLocalTerminalModes, resolveCssColorForXterm, resolveTerminalTheme, snapTerminalViewportToBottom, terminalShortcutLabel };
|
package/dist/locale/messages.js
CHANGED
|
@@ -162,6 +162,7 @@ var zhCN = {
|
|
|
162
162
|
"log.copy": "复制全文",
|
|
163
163
|
"log.copyMatches": "复制匹配行",
|
|
164
164
|
"log.copied": "已复制",
|
|
165
|
+
"log.copyFailed": "复制失败,请用 HTTPS 或手动选择正文",
|
|
165
166
|
"log.filter": "按级别过滤",
|
|
166
167
|
"log.newLines": "新增 {count} 行",
|
|
167
168
|
"log.lineNo": "第 {n} 行",
|
|
@@ -391,6 +392,7 @@ var enUS = {
|
|
|
391
392
|
"log.copy": "Copy all",
|
|
392
393
|
"log.copyMatches": "Copy matching lines",
|
|
393
394
|
"log.copied": "Copied",
|
|
395
|
+
"log.copyFailed": "Copy failed. Use HTTPS or select the log text.",
|
|
394
396
|
"log.filter": "Filter levels",
|
|
395
397
|
"log.newLines": "{count} new lines",
|
|
396
398
|
"log.lineNo": "Line {n}",
|
|
@@ -11,7 +11,9 @@ export declare function readClipboardText(): Promise<string | null>;
|
|
|
11
11
|
export declare function writeClipboardText(text: string): Promise<boolean>;
|
|
12
12
|
/** execCommand 兜底:CEF / 无 Clipboard API 权限时菜单复制仍可用 */
|
|
13
13
|
export declare function copyTextWithExecCommand(text: string): boolean;
|
|
14
|
-
/**
|
|
14
|
+
/** HTTP / 非安全上下文没有 Clipboard API,writeText 会 SecurityError。 */
|
|
15
|
+
export declare function isInsecureClipboardContext(): boolean;
|
|
16
|
+
/** 写入系统剪贴板。HTTP 先同步 execCommand,避免 await 丢掉用户手势。 */
|
|
15
17
|
export declare function copyTextToClipboard(text: string): Promise<boolean>;
|
|
16
18
|
/** @internal vitest 用 */
|
|
17
19
|
export declare function __resetClipboardStateForTests(): void;
|
|
@@ -165,9 +165,11 @@ function copyTextWithExecCommand(text) {
|
|
|
165
165
|
const textarea = document.createElement("textarea");
|
|
166
166
|
textarea.value = text;
|
|
167
167
|
textarea.setAttribute("readonly", "");
|
|
168
|
-
textarea.style.cssText = "position:fixed;left
|
|
168
|
+
textarea.style.cssText = "position:fixed;left:0;top:0;width:1px;height:1px;padding:0;border:0;opacity:0.01";
|
|
169
169
|
document.body.appendChild(textarea);
|
|
170
|
+
if (typeof textarea.focus === "function") textarea.focus();
|
|
170
171
|
textarea.select();
|
|
172
|
+
if (typeof textarea.setSelectionRange === "function") textarea.setSelectionRange(0, text.length);
|
|
171
173
|
const ok = document.execCommand("copy");
|
|
172
174
|
textarea.remove();
|
|
173
175
|
return ok;
|
|
@@ -175,13 +177,21 @@ function copyTextWithExecCommand(text) {
|
|
|
175
177
|
return false;
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
|
-
/**
|
|
180
|
+
/** HTTP / 非安全上下文没有 Clipboard API,writeText 会 SecurityError。 */
|
|
181
|
+
function isInsecureClipboardContext() {
|
|
182
|
+
return typeof globalThis !== "undefined" && globalThis.isSecureContext === false;
|
|
183
|
+
}
|
|
184
|
+
/** 写入系统剪贴板。HTTP 先同步 execCommand,避免 await 丢掉用户手势。 */
|
|
179
185
|
async function copyTextToClipboard(text) {
|
|
180
186
|
if (!text) return false;
|
|
187
|
+
if (isInsecureClipboardContext()) {
|
|
188
|
+
if (copyTextWithExecCommand(text)) return true;
|
|
189
|
+
return writeClipboardViaShell(text);
|
|
190
|
+
}
|
|
181
191
|
await syncClipboardPermissionState();
|
|
182
192
|
if (!writeApiBlocked && await writeClipboardText(text)) return true;
|
|
183
193
|
if (copyTextWithExecCommand(text)) return true;
|
|
184
194
|
return writeClipboardViaShell(text);
|
|
185
195
|
}
|
|
186
196
|
//#endregion
|
|
187
|
-
export { beginClipboardPrefetch, copyTextToClipboard, copyTextWithExecCommand, prefetchClipboardText, readClipboardText, waitClipboardPrefetch, writeClipboardText };
|
|
197
|
+
export { beginClipboardPrefetch, copyTextToClipboard, copyTextWithExecCommand, isInsecureClipboardContext, prefetchClipboardText, readClipboardText, waitClipboardPrefetch, writeClipboardText };
|