@xcanwin/manyoyo 5.8.10 → 5.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/web/frontend/app.css
CHANGED
|
@@ -352,6 +352,20 @@ textarea:focus-visible {
|
|
|
352
352
|
white-space: pre-wrap;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
.link-confirm-url {
|
|
356
|
+
margin-top: 2px;
|
|
357
|
+
padding: 12px 14px;
|
|
358
|
+
border: 1px solid rgba(146, 100, 42, 0.18);
|
|
359
|
+
border-radius: 10px;
|
|
360
|
+
background: #fff8ef;
|
|
361
|
+
color: var(--text);
|
|
362
|
+
font-family: var(--font-mono);
|
|
363
|
+
font-size: 12px;
|
|
364
|
+
line-height: 1.6;
|
|
365
|
+
word-break: break-all;
|
|
366
|
+
user-select: text;
|
|
367
|
+
}
|
|
368
|
+
|
|
355
369
|
.config-editor {
|
|
356
370
|
width: 100%;
|
|
357
371
|
min-height: 340px;
|
|
@@ -275,6 +275,22 @@
|
|
|
275
275
|
</footer>
|
|
276
276
|
</section>
|
|
277
277
|
</div>
|
|
278
|
+
|
|
279
|
+
<div id="externalLinkModal" class="modal-backdrop" hidden>
|
|
280
|
+
<section class="modal" role="dialog" aria-modal="true" aria-labelledby="externalLinkTitle">
|
|
281
|
+
<header class="modal-header">
|
|
282
|
+
<h2 id="externalLinkTitle">打开外部链接</h2>
|
|
283
|
+
<button type="button" id="externalLinkCancelBtn" class="secondary">取消</button>
|
|
284
|
+
</header>
|
|
285
|
+
<div class="modal-body">
|
|
286
|
+
<div class="modal-tip">即将新标签页打开外部页面。请确认完整链接可信后再继续。</div>
|
|
287
|
+
<div id="externalLinkUrl" class="link-confirm-url"></div>
|
|
288
|
+
</div>
|
|
289
|
+
<footer class="modal-footer">
|
|
290
|
+
<button type="button" id="externalLinkOpenBtn">确认并新标签打开</button>
|
|
291
|
+
</footer>
|
|
292
|
+
</section>
|
|
293
|
+
</div>
|
|
278
294
|
</div>
|
|
279
295
|
|
|
280
296
|
<script src="/app/vendor/xterm.js"></script>
|
package/lib/web/frontend/app.js
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
configModalOpen: false,
|
|
52
52
|
createModalOpen: false,
|
|
53
53
|
agentTemplateModalOpen: false,
|
|
54
|
+
externalLinkModalOpen: false,
|
|
54
55
|
configLoading: false,
|
|
55
56
|
configSaving: false,
|
|
56
57
|
configSaveMessage: '',
|
|
@@ -105,7 +106,8 @@
|
|
|
105
106
|
lastSentRows: 0,
|
|
106
107
|
ctrlMode: false,
|
|
107
108
|
altMode: false
|
|
108
|
-
}
|
|
109
|
+
},
|
|
110
|
+
externalLinkUrl: ''
|
|
109
111
|
};
|
|
110
112
|
|
|
111
113
|
const sidebarNode = document.querySelector('.sidebar');
|
|
@@ -196,6 +198,10 @@
|
|
|
196
198
|
const agentTemplateCancelBtn = document.getElementById('agentTemplateCancelBtn');
|
|
197
199
|
const agentTemplateResetBtn = document.getElementById('agentTemplateResetBtn');
|
|
198
200
|
const agentTemplateSaveBtn = document.getElementById('agentTemplateSaveBtn');
|
|
201
|
+
const externalLinkModal = document.getElementById('externalLinkModal');
|
|
202
|
+
const externalLinkUrl = document.getElementById('externalLinkUrl');
|
|
203
|
+
const externalLinkCancelBtn = document.getElementById('externalLinkCancelBtn');
|
|
204
|
+
const externalLinkOpenBtn = document.getElementById('externalLinkOpenBtn');
|
|
199
205
|
const refreshBtn = document.getElementById('refreshBtn');
|
|
200
206
|
const removeBtn = document.getElementById('removeBtn');
|
|
201
207
|
const removeAllBtn = document.getElementById('removeAllBtn');
|
|
@@ -740,6 +746,45 @@
|
|
|
740
746
|
configStatus.textContent = text;
|
|
741
747
|
}
|
|
742
748
|
|
|
749
|
+
function openExternalLinkModalView(url) {
|
|
750
|
+
const text = String(url || '').trim();
|
|
751
|
+
if (!text) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
state.externalLinkUrl = text;
|
|
755
|
+
state.externalLinkModalOpen = true;
|
|
756
|
+
if (externalLinkUrl) {
|
|
757
|
+
externalLinkUrl.textContent = text;
|
|
758
|
+
}
|
|
759
|
+
setModalVisible(externalLinkModal, true);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function closeExternalLinkModalView() {
|
|
763
|
+
state.externalLinkModalOpen = false;
|
|
764
|
+
state.externalLinkUrl = '';
|
|
765
|
+
if (externalLinkUrl) {
|
|
766
|
+
externalLinkUrl.textContent = '';
|
|
767
|
+
}
|
|
768
|
+
setModalVisible(externalLinkModal, false);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function confirmExternalLinkOpen() {
|
|
772
|
+
const targetUrl = String(state.externalLinkUrl || '').trim();
|
|
773
|
+
if (!targetUrl) {
|
|
774
|
+
closeExternalLinkModalView();
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (markdownRenderer && typeof markdownRenderer.openExternalLink === 'function') {
|
|
778
|
+
markdownRenderer.openExternalLink(targetUrl);
|
|
779
|
+
} else {
|
|
780
|
+
const popup = window.open(targetUrl, '_blank', 'noopener,noreferrer');
|
|
781
|
+
if (popup) {
|
|
782
|
+
popup.opener = null;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
closeExternalLinkModalView();
|
|
786
|
+
}
|
|
787
|
+
|
|
743
788
|
function showDirectoryPickerError(message) {
|
|
744
789
|
if (!directoryPickerError) return;
|
|
745
790
|
const text = String(message || '').trim();
|
|
@@ -2095,6 +2140,9 @@
|
|
|
2095
2140
|
if (agentTemplateModal) {
|
|
2096
2141
|
agentTemplateModal.hidden = !state.agentTemplateModalOpen;
|
|
2097
2142
|
}
|
|
2143
|
+
if (externalLinkModal) {
|
|
2144
|
+
externalLinkModal.hidden = !state.externalLinkModalOpen;
|
|
2145
|
+
}
|
|
2098
2146
|
if (agentTemplateSaveBtn) {
|
|
2099
2147
|
agentTemplateSaveBtn.disabled = state.agentTemplateSaving || !state.active;
|
|
2100
2148
|
}
|
|
@@ -2118,7 +2166,7 @@
|
|
|
2118
2166
|
}
|
|
2119
2167
|
document.body.classList.toggle(
|
|
2120
2168
|
'modal-open',
|
|
2121
|
-
state.configModalOpen || state.createModalOpen || state.directoryPicker.open || state.agentTemplateModalOpen
|
|
2169
|
+
state.configModalOpen || state.createModalOpen || state.directoryPicker.open || state.agentTemplateModalOpen || state.externalLinkModalOpen
|
|
2122
2170
|
);
|
|
2123
2171
|
if (!state.active) {
|
|
2124
2172
|
sendState.textContent = '未选择会话';
|
|
@@ -4030,6 +4078,15 @@
|
|
|
4030
4078
|
});
|
|
4031
4079
|
}
|
|
4032
4080
|
|
|
4081
|
+
if (externalLinkModal) {
|
|
4082
|
+
externalLinkModal.addEventListener('click', function (event) {
|
|
4083
|
+
if (event.target === externalLinkModal) {
|
|
4084
|
+
closeExternalLinkModalView();
|
|
4085
|
+
syncUi();
|
|
4086
|
+
}
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
|
|
4033
4090
|
window.addEventListener('keydown', function (event) {
|
|
4034
4091
|
if (event.key === 'Escape' && state.configModalOpen) {
|
|
4035
4092
|
closeConfigModal();
|
|
@@ -4046,6 +4103,10 @@
|
|
|
4046
4103
|
closeAgentTemplateModal();
|
|
4047
4104
|
syncUi();
|
|
4048
4105
|
}
|
|
4106
|
+
if (event.key === 'Escape' && state.externalLinkModalOpen) {
|
|
4107
|
+
closeExternalLinkModalView();
|
|
4108
|
+
syncUi();
|
|
4109
|
+
}
|
|
4049
4110
|
if (event.key === 'Escape' && state.mobileSidebarOpen) {
|
|
4050
4111
|
closeMobileSessionPanel();
|
|
4051
4112
|
}
|
|
@@ -4128,6 +4189,27 @@
|
|
|
4128
4189
|
}
|
|
4129
4190
|
});
|
|
4130
4191
|
|
|
4192
|
+
if (externalLinkCancelBtn) {
|
|
4193
|
+
externalLinkCancelBtn.addEventListener('click', function () {
|
|
4194
|
+
closeExternalLinkModalView();
|
|
4195
|
+
syncUi();
|
|
4196
|
+
});
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
if (externalLinkOpenBtn) {
|
|
4200
|
+
externalLinkOpenBtn.addEventListener('click', function () {
|
|
4201
|
+
confirmExternalLinkOpen();
|
|
4202
|
+
syncUi();
|
|
4203
|
+
});
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
if (markdownRenderer && typeof markdownRenderer.setLinkOpenHandler === 'function') {
|
|
4207
|
+
markdownRenderer.setLinkOpenHandler(function (url) {
|
|
4208
|
+
openExternalLinkModalView(url);
|
|
4209
|
+
syncUi();
|
|
4210
|
+
});
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4131
4213
|
window.addEventListener('beforeunload', function () {
|
|
4132
4214
|
disconnectTerminal('', true);
|
|
4133
4215
|
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
(function () {
|
|
2
|
-
const
|
|
2
|
+
const MARKDOWN_LINK_PROTOCOL_PATTERN = /^(https?:|mailto:|tel:)/i;
|
|
3
|
+
const MARKDOWN_IMAGE_PROTOCOL_PATTERN = /^(https?:)/i;
|
|
3
4
|
const runtime = {
|
|
4
5
|
configured: false,
|
|
5
|
-
available: false
|
|
6
|
+
available: false,
|
|
7
|
+
linkGuardBound: false,
|
|
8
|
+
linkOpenHandler: null
|
|
6
9
|
};
|
|
7
10
|
|
|
8
11
|
function escapeHtml(value) {
|
|
@@ -14,26 +17,175 @@
|
|
|
14
17
|
.replace(/'/g, ''');
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
function
|
|
20
|
+
function normalizeRendererValue(value) {
|
|
21
|
+
if (value && typeof value === 'object') {
|
|
22
|
+
if (typeof value.href === 'string') {
|
|
23
|
+
return value.href;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value.text === 'string') {
|
|
26
|
+
return value.text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return String(value == null ? '' : value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sanitizeMarkdownLinkUrl(value) {
|
|
18
33
|
const raw = String(value == null ? '' : value).trim();
|
|
19
34
|
if (!raw) {
|
|
20
35
|
return '';
|
|
21
36
|
}
|
|
22
|
-
if (raw
|
|
37
|
+
if (MARKDOWN_LINK_PROTOCOL_PATTERN.test(raw)) {
|
|
23
38
|
return raw;
|
|
24
39
|
}
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function sanitizeMarkdownImageUrl(value) {
|
|
44
|
+
const raw = String(value == null ? '' : value).trim();
|
|
45
|
+
if (!raw) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
25
48
|
if (raw[0] === '/') {
|
|
26
49
|
return raw.startsWith('//') ? '' : raw;
|
|
27
50
|
}
|
|
28
51
|
if (raw.startsWith('./') || raw.startsWith('../')) {
|
|
29
52
|
return raw;
|
|
30
53
|
}
|
|
31
|
-
if (
|
|
54
|
+
if (MARKDOWN_IMAGE_PROTOCOL_PATTERN.test(raw)) {
|
|
32
55
|
return raw;
|
|
33
56
|
}
|
|
34
57
|
return '';
|
|
35
58
|
}
|
|
36
59
|
|
|
60
|
+
function getRendererToken(value) {
|
|
61
|
+
return value && typeof value === 'object' ? value : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renderInlineTokens(rendererContext, token, fallbackText) {
|
|
65
|
+
if (
|
|
66
|
+
token
|
|
67
|
+
&& Array.isArray(token.tokens)
|
|
68
|
+
&& rendererContext
|
|
69
|
+
&& rendererContext.parser
|
|
70
|
+
&& typeof rendererContext.parser.parseInline === 'function'
|
|
71
|
+
) {
|
|
72
|
+
try {
|
|
73
|
+
return String(rendererContext.parser.parseInline(token.tokens) || '');
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// ignore and fallback to plain text below
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (token && typeof token.text === 'string') {
|
|
79
|
+
return escapeHtml(token.text);
|
|
80
|
+
}
|
|
81
|
+
return escapeHtml(fallbackText || '');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildSafeAnchorHtml(href, title, content, extraAttrs) {
|
|
85
|
+
let output = '<a href="' + escapeHtml(href) + '" target="_blank" rel="noopener noreferrer"'
|
|
86
|
+
+ ' referrerpolicy="no-referrer" data-safe-external-link="true"'
|
|
87
|
+
+ ' data-safe-href="' + escapeHtml(href) + '"';
|
|
88
|
+
if (title) {
|
|
89
|
+
output += ' title="' + escapeHtml(title) + '"';
|
|
90
|
+
}
|
|
91
|
+
if (extraAttrs) {
|
|
92
|
+
output += extraAttrs;
|
|
93
|
+
}
|
|
94
|
+
output += '>' + content + '</a>';
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function openExternalLinkWithNoReferrer(href) {
|
|
99
|
+
const url = String(href || '').trim();
|
|
100
|
+
if (!url || typeof document === 'undefined' || !document || typeof document.createElement !== 'function') {
|
|
101
|
+
if (typeof window.open === 'function') {
|
|
102
|
+
const opened = window.open(url, '_blank', 'noopener,noreferrer');
|
|
103
|
+
if (opened) {
|
|
104
|
+
opened.opener = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const anchor = document.createElement('a');
|
|
111
|
+
anchor.href = url;
|
|
112
|
+
anchor.target = '_blank';
|
|
113
|
+
anchor.rel = 'noopener noreferrer';
|
|
114
|
+
anchor.referrerPolicy = 'no-referrer';
|
|
115
|
+
anchor.style.display = 'none';
|
|
116
|
+
document.body.appendChild(anchor);
|
|
117
|
+
try {
|
|
118
|
+
if (typeof anchor.click === 'function') {
|
|
119
|
+
anchor.click();
|
|
120
|
+
} else if (typeof window.open === 'function') {
|
|
121
|
+
const opened = window.open(url, '_blank', 'noopener,noreferrer');
|
|
122
|
+
if (opened) {
|
|
123
|
+
opened.opener = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
if (anchor.parentNode && typeof anchor.parentNode.removeChild === 'function') {
|
|
128
|
+
anchor.parentNode.removeChild(anchor);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function requestExternalLinkOpen(href) {
|
|
134
|
+
const url = String(href || '').trim();
|
|
135
|
+
if (!url) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (typeof runtime.linkOpenHandler === 'function') {
|
|
139
|
+
runtime.linkOpenHandler(url);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const shouldOpen = typeof window.confirm === 'function'
|
|
144
|
+
? window.confirm('即将打开外部链接:\n' + url + '\n\n确认继续打开?')
|
|
145
|
+
: true;
|
|
146
|
+
if (!shouldOpen) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
openExternalLinkWithNoReferrer(url);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function bindDocumentLinkGuard() {
|
|
154
|
+
if (runtime.linkGuardBound || typeof document === 'undefined' || !document || typeof document.addEventListener !== 'function') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
document.addEventListener('click', function (event) {
|
|
159
|
+
const target = event && event.target;
|
|
160
|
+
if (!target || typeof target.closest !== 'function') {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const link = target.closest('a[data-safe-external-link="true"]');
|
|
164
|
+
if (!link) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (event.preventDefault) {
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
}
|
|
170
|
+
if (event.stopPropagation) {
|
|
171
|
+
event.stopPropagation();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const href = String(
|
|
175
|
+
(typeof link.getAttribute === 'function' && (link.getAttribute('data-safe-href') || link.getAttribute('href')))
|
|
176
|
+
|| link.href
|
|
177
|
+
|| ''
|
|
178
|
+
).trim();
|
|
179
|
+
if (!href) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
requestExternalLinkOpen(href);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
runtime.linkGuardBound = true;
|
|
187
|
+
}
|
|
188
|
+
|
|
37
189
|
function getMarkedApi() {
|
|
38
190
|
const api = window.marked;
|
|
39
191
|
if (!api || typeof api.parse !== 'function') {
|
|
@@ -57,45 +209,48 @@
|
|
|
57
209
|
try {
|
|
58
210
|
const renderer = new markedApi.Renderer();
|
|
59
211
|
renderer.html = function (html) {
|
|
60
|
-
|
|
212
|
+
const token = getRendererToken(html);
|
|
213
|
+
return escapeHtml(token ? token.text : html);
|
|
61
214
|
};
|
|
62
215
|
renderer.link = function (href, title, text) {
|
|
63
|
-
const
|
|
216
|
+
const token = getRendererToken(href);
|
|
217
|
+
const rawHref = token ? token.href : normalizeRendererValue(href);
|
|
218
|
+
const rawTitle = token ? token.title : title;
|
|
219
|
+
const safeHref = sanitizeMarkdownLinkUrl(rawHref);
|
|
220
|
+
const safeText = renderInlineTokens(this, token, text)
|
|
221
|
+
// [P1-02] 移除 marked 已渲染链接文本中的 on* 事件属性,防止内联 HTML 注入 XSS
|
|
222
|
+
.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, '');
|
|
64
223
|
if (!safeHref) {
|
|
65
|
-
return escapeHtml(text || '');
|
|
66
|
-
}
|
|
67
|
-
// [P1-02] 移除 marked 已渲染链接文本中的 on* 事件属性,防止内联 HTML 注入 XSS
|
|
68
|
-
const safeText = String(text || '').replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, '');
|
|
69
|
-
let output = '<a href="' + escapeHtml(safeHref) + '" target="_blank" rel="noopener noreferrer"';
|
|
70
|
-
if (title) {
|
|
71
|
-
output += ' title="' + escapeHtml(title) + '"';
|
|
224
|
+
return safeText || escapeHtml(token ? token.text : text || '');
|
|
72
225
|
}
|
|
73
|
-
|
|
74
|
-
return output;
|
|
226
|
+
return buildSafeAnchorHtml(safeHref, rawTitle, safeText || escapeHtml(safeHref));
|
|
75
227
|
};
|
|
76
228
|
// [P1-01] 重写 image 渲染器:
|
|
77
229
|
// - 外部 http/https 图片转为可点击链接,避免浏览器自动发起外部请求(追踪像素风险)
|
|
78
230
|
// - 相对路径图片正常渲染为 <img>
|
|
79
231
|
// - 危险协议(javascript:/data: 等)降级为纯文本
|
|
80
232
|
renderer.image = function (href, title, text) {
|
|
81
|
-
const
|
|
233
|
+
const token = getRendererToken(href);
|
|
234
|
+
const rawHref = token ? token.href : normalizeRendererValue(href);
|
|
235
|
+
const rawTitle = token ? token.title : title;
|
|
236
|
+
const safeHref = sanitizeMarkdownImageUrl(rawHref);
|
|
237
|
+
const safeText = renderInlineTokens(this, token, text)
|
|
238
|
+
.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, '');
|
|
82
239
|
if (!safeHref) {
|
|
83
|
-
return escapeHtml(text || '');
|
|
240
|
+
return safeText || escapeHtml(token ? token.text : text || '');
|
|
84
241
|
}
|
|
85
242
|
// 外部绝对 URL:转为链接,用户主动决定是否访问
|
|
86
243
|
if (/^https?:/i.test(safeHref)) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
return output + '>[\uD83D\uDDBC\uFE0F点击查看图片:' + safeText + ']</a>';
|
|
244
|
+
return buildSafeAnchorHtml(
|
|
245
|
+
safeHref,
|
|
246
|
+
rawTitle,
|
|
247
|
+
'[\uD83D\uDDBC\uFE0F点击查看图片:' + (safeText || escapeHtml(safeHref)) + ']'
|
|
248
|
+
);
|
|
94
249
|
}
|
|
95
250
|
// 相对路径:正常渲染为图片
|
|
96
251
|
let output = '<img src="' + escapeHtml(safeHref) + '" alt="' + escapeHtml(text || '') + '"';
|
|
97
|
-
if (
|
|
98
|
-
output += ' title="' + escapeHtml(
|
|
252
|
+
if (rawTitle) {
|
|
253
|
+
output += ' title="' + escapeHtml(rawTitle) + '"';
|
|
99
254
|
}
|
|
100
255
|
return output + '>';
|
|
101
256
|
};
|
|
@@ -138,6 +293,12 @@
|
|
|
138
293
|
|
|
139
294
|
window.ManyoyoMarkdown = {
|
|
140
295
|
shouldRenderMessage,
|
|
141
|
-
render
|
|
296
|
+
render,
|
|
297
|
+
openExternalLink: openExternalLinkWithNoReferrer,
|
|
298
|
+
setLinkOpenHandler: function (handler) {
|
|
299
|
+
runtime.linkOpenHandler = typeof handler === 'function' ? handler : null;
|
|
300
|
+
}
|
|
142
301
|
};
|
|
302
|
+
|
|
303
|
+
bindDocumentLinkGuard();
|
|
143
304
|
}());
|
|
@@ -72,5 +72,13 @@
|
|
|
72
72
|
|
|
73
73
|
.bubble .md-content a {
|
|
74
74
|
color: #8a4f17;
|
|
75
|
-
text-decoration-
|
|
75
|
+
text-decoration-line: underline;
|
|
76
|
+
text-decoration-thickness: 2px;
|
|
77
|
+
text-underline-offset: 0.16em;
|
|
78
|
+
text-decoration-color: rgba(138, 79, 23, 0.8);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.bubble .md-content a:hover,
|
|
82
|
+
.bubble .md-content a:focus-visible {
|
|
83
|
+
text-decoration-color: #8a4f17;
|
|
76
84
|
}
|