niuma-ui 1.3.2 → 1.3.4

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 CHANGED
@@ -6,6 +6,18 @@
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.3.4] - 2026-09-11
10
+
11
+ ### 修复
12
+
13
+ - `RsLog` 在 HTTP(非安全上下文)下复制:不再先走会失败的 Clipboard API(`await` 会丢掉用户手势),改为同步 `execCommand`;级别/行号不可选中,避免复制出「输出」「信息」。失败时工具栏提示复制失败。
14
+
15
+ ## [1.3.3] - 2026-09-09
16
+
17
+ ### 修复
18
+
19
+ - `RsLog` 开启 `wrap` 时不再把每一行拉成整栏高度,避免日志区上半空白、正文挤在底部。Playground `/log` 增加撑满栏折行示例;`/#/visual/rs-log` + `pnpm test:visual` 用几何断言锁行高(jsdom 测不到)。
20
+
9
21
  ## [1.3.2] - 2026-09-09
10
22
 
11
23
  ### 修复
@@ -1,5 +1,5 @@
1
1
 
2
- .rs-log[data-v-292b0834] {
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-292b0834] {
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-292b0834] {
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-292b0834] {
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-292b0834] {
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-292b0834] {
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,54 +50,63 @@
50
50
  font-size: var(--rs-font-size-xs);
51
51
  cursor: pointer;
52
52
  }
53
- .rs-log__chip--on[data-v-292b0834] {
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-292b0834] {
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-292b0834]:focus-visible {
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-292b0834],
69
- .rs-log__plain[data-v-292b0834] {
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-292b0834] {
75
+ .rs-log__plain[data-v-0c4db684] {
76
76
  overflow: auto;
77
77
  }
78
- .rs-log__row[data-v-292b0834] {
78
+ .rs-log__row[data-v-0c4db684] {
79
79
  display: flex;
80
80
  align-items: flex-start;
81
81
  gap: 0.55rem;
82
82
  box-sizing: border-box;
83
- min-height: 100%;
84
83
  padding: 0 0.7rem;
85
84
  white-space: nowrap;
86
85
  color: var(--rs-log-row-fg, var(--rs-log-fg));
87
86
  }
88
- .rs-log--wrap .rs-log__row[data-v-292b0834] {
87
+ .rs-log:not(.rs-log--wrap) .rs-log__list .rs-log__row[data-v-0c4db684] {
88
+ min-height: 100%;
89
+ }
90
+ .rs-log--wrap .rs-log__plain[data-v-0c4db684] {
91
+ display: block;
92
+ }
93
+ .rs-log--wrap .rs-log__row[data-v-0c4db684] {
94
+ height: auto;
95
+ max-height: none;
96
+ min-height: 0;
89
97
  white-space: pre-wrap;
90
98
  overflow-wrap: anywhere;
99
+ word-break: break-word;
91
100
  padding-block: 0.25rem;
92
101
  }
93
- .rs-log--zebra .rs-log__row--odd[data-v-292b0834] {
102
+ .rs-log--zebra .rs-log__row--odd[data-v-0c4db684] {
94
103
  background: var(--rs-log-stripe, transparent);
95
104
  }
96
- .rs-log__row--active[data-v-292b0834] {
105
+ .rs-log__row--active[data-v-0c4db684] {
97
106
  background: color-mix(in srgb, var(--rs-primary) 12%, var(--rs-log-bg, transparent));
98
107
  box-shadow: inset 2px 0 0 var(--rs-primary);
99
108
  }
100
- .rs-log__marker[data-v-292b0834] {
109
+ .rs-log__marker[data-v-0c4db684] {
101
110
  flex-shrink: 0;
102
111
  width: 0.2rem;
103
112
  align-self: stretch;
@@ -105,42 +114,47 @@
105
114
  margin-block: 0.2rem;
106
115
  border-radius: 99px;
107
116
  background: currentColor;
117
+ user-select: none;
108
118
  }
109
- .rs-log__no[data-v-292b0834],
110
- .rs-log__time[data-v-292b0834] {
119
+ .rs-log__no[data-v-0c4db684],
120
+ .rs-log__time[data-v-0c4db684] {
111
121
  flex-shrink: 0;
112
122
  color: var(--rs-log-muted, var(--rs-text-muted));
113
123
  font-variant-numeric: tabular-nums;
114
124
  unicode-bidi: isolate;
125
+ user-select: none;
115
126
  }
116
- .rs-log__level[data-v-292b0834] {
127
+ .rs-log__level[data-v-0c4db684] {
117
128
  flex-shrink: 0;
118
129
  min-width: 3.6rem;
119
130
  font-weight: var(--rs-font-weight-semibold);
120
131
  letter-spacing: 0.02em;
121
132
  unicode-bidi: isolate;
133
+ user-select: none;
122
134
  }
123
- .rs-log__text[data-v-292b0834] {
135
+ .rs-log__text[data-v-0c4db684] {
124
136
  min-width: 0;
125
137
  flex: 1;
126
138
  font-weight: inherit;
127
139
  unicode-bidi: plaintext;
128
140
  user-select: text;
129
141
  }
130
- .rs-log--wrap .rs-log__text[data-v-292b0834] {
131
- overflow: hidden;
142
+ .rs-log--wrap .rs-log__text[data-v-0c4db684] {
143
+ overflow: visible;
144
+ height: auto;
145
+ min-height: 0;
132
146
  }
133
- .rs-log:not(.rs-log--wrap) .rs-log__text[data-v-292b0834] {
147
+ .rs-log:not(.rs-log--wrap) .rs-log__text[data-v-0c4db684] {
134
148
  overflow: hidden;
135
149
  text-overflow: ellipsis;
136
150
  }
137
- .rs-log__hit[data-v-292b0834] {
151
+ .rs-log__hit[data-v-0c4db684] {
138
152
  padding: 0 0.1em;
139
153
  background: var(--rs-log-hit-bg);
140
154
  color: var(--rs-log-hit-fg);
141
155
  font: inherit;
142
156
  }
143
- .rs-log__sr-only[data-v-292b0834] {
157
+ .rs-log__sr-only[data-v-0c4db684] {
144
158
  position: absolute;
145
159
  width: 1px;
146
160
  height: 1px;
@@ -151,47 +165,47 @@
151
165
  white-space: nowrap;
152
166
  border: 0;
153
167
  }
154
- .rs-log__row--plain[data-v-292b0834] {
168
+ .rs-log__row--plain[data-v-0c4db684] {
155
169
  --rs-log-row-fg: var(--rs-log-fg);
156
170
  font-weight: var(--rs-font-weight-regular);
157
171
  }
158
- .rs-log__row--trace[data-v-292b0834] {
172
+ .rs-log__row--trace[data-v-0c4db684] {
159
173
  --rs-log-row-fg: var(--rs-log-trace);
160
174
  font-weight: var(--rs-font-weight-regular);
161
175
  }
162
- .rs-log__row--debug[data-v-292b0834] {
176
+ .rs-log__row--debug[data-v-0c4db684] {
163
177
  --rs-log-row-fg: var(--rs-log-debug);
164
178
  font-weight: var(--rs-font-weight-regular);
165
179
  }
166
- .rs-log__row--info[data-v-292b0834],
167
- .rs-log__row--notice[data-v-292b0834] {
180
+ .rs-log__row--info[data-v-0c4db684],
181
+ .rs-log__row--notice[data-v-0c4db684] {
168
182
  --rs-log-row-fg: var(--rs-log-info);
169
183
  font-weight: var(--rs-font-weight-medium);
170
184
  }
171
- .rs-log__row--notice[data-v-292b0834] {
185
+ .rs-log__row--notice[data-v-0c4db684] {
172
186
  --rs-log-row-fg: var(--rs-log-notice);
173
187
  }
174
- .rs-log__row--success[data-v-292b0834] {
188
+ .rs-log__row--success[data-v-0c4db684] {
175
189
  --rs-log-row-fg: var(--rs-log-success);
176
190
  font-weight: var(--rs-font-weight-medium);
177
191
  }
178
- .rs-log__row--warn[data-v-292b0834] {
192
+ .rs-log__row--warn[data-v-0c4db684] {
179
193
  --rs-log-row-fg: var(--rs-log-warn);
180
194
  font-weight: var(--rs-font-weight-semibold);
181
195
  }
182
- .rs-log__row--error[data-v-292b0834] {
196
+ .rs-log__row--error[data-v-0c4db684] {
183
197
  --rs-log-row-fg: var(--rs-log-error);
184
198
  font-weight: var(--rs-font-weight-bold);
185
199
  }
186
- .rs-log__row--fatal[data-v-292b0834] {
200
+ .rs-log__row--fatal[data-v-0c4db684] {
187
201
  --rs-log-row-fg: var(--rs-log-fatal);
188
202
  font-weight: var(--rs-font-weight-bold);
189
203
  }
190
204
  @media (forced-colors: active) {
191
- .rs-log__marker[data-v-292b0834] {
205
+ .rs-log__marker[data-v-0c4db684] {
192
206
  background: ButtonText;
193
207
  }
194
- .rs-log__row--active[data-v-292b0834] {
208
+ .rs-log__row--active[data-v-0c4db684] {
195
209
  outline: 2px solid Highlight;
196
210
  outline-offset: -2px;
197
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() {
@@ -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-292b0834"]]);
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 };
@@ -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
- /** 写入系统剪贴板(API 优先,execCommand 兜底) */
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:-9999px;top:0;opacity:0";
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
- /** 写入系统剪贴板(API 优先,execCommand 兜底) */
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niuma-ui",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "description": "Vue 3 工作台设计系统(Rs* 组件、--rs-* token;含可选编辑器 / 终端)",