fraim-hub 2.0.233 → 2.0.234
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/package.json +2 -2
- package/public/ai-hub/script.js +34 -0
- package/public/ai-hub/styles.css +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.234",
|
|
4
4
|
"description": "FRAIM Hub local companion package.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fraim-hub": "bin/fraim-hub.js"
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"electron": "^41.2.2",
|
|
159
159
|
"electron-updater": "^6.8.9",
|
|
160
160
|
"express": "^5.2.1",
|
|
161
|
-
"fraim": "2.0.
|
|
161
|
+
"fraim": "2.0.234",
|
|
162
162
|
"mongodb": "^7.0.0",
|
|
163
163
|
"node-cron": "4.2.1",
|
|
164
164
|
"node-edge-tts": "^1.2.10",
|
package/public/ai-hub/script.js
CHANGED
|
@@ -1311,6 +1311,19 @@ function stripHubInjectedPromptBlocks(text) {
|
|
|
1311
1311
|
.trim();
|
|
1312
1312
|
}
|
|
1313
1313
|
|
|
1314
|
+
// Returns a validated http/https URL string suitable for use in an href, or null if unsafe.
|
|
1315
|
+
function _safeHref(raw) {
|
|
1316
|
+
try {
|
|
1317
|
+
const url = new URL(raw);
|
|
1318
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') ? url.href : null;
|
|
1319
|
+
} catch (_) { return null; }
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// Escape & and " in a URL for safe insertion into an href="..." attribute.
|
|
1323
|
+
function _escapeHref(href) {
|
|
1324
|
+
return href.replace(/&/g, '&').replace(/"/g, '"');
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1314
1327
|
// R8: render markdown subset safely. HTML is escaped first.
|
|
1315
1328
|
function formatEmployeeText(text) {
|
|
1316
1329
|
if (!text) return '';
|
|
@@ -1326,6 +1339,27 @@ function formatEmployeeText(text) {
|
|
|
1326
1339
|
s = s.replace(/```[\w]*\n?([\s\S]*?)```/g, (_, code) => `<pre><code>${code.trimEnd()}</code></pre>`);
|
|
1327
1340
|
// 3. Inline code (single backtick, non-greedy, no newlines).
|
|
1328
1341
|
s = s.replace(/`([^`\n]+)`/g, '<code>$1</code>');
|
|
1342
|
+
// 3.5. Markdown links [text](url) — only http/https hrefs allowed; link text is already HTML-escaped.
|
|
1343
|
+
// Empty-text links and non-absolute URLs fall back to plain text.
|
|
1344
|
+
s = s.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (match, linkText, rawUrl) => {
|
|
1345
|
+
const trimmed = linkText.trim();
|
|
1346
|
+
// Empty link text: render as plain text; sentinel prevents step 3.6 auto-linkify.
|
|
1347
|
+
if (!trimmed) return `\x00NOLINK\x00${rawUrl.trim()}\x00ENDNOLINK\x00`;
|
|
1348
|
+
// Reverse HTML entities step 1 introduced inside the URL before structural validation.
|
|
1349
|
+
const decodedUrl = rawUrl.trim().replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
1350
|
+
const href = _safeHref(decodedUrl);
|
|
1351
|
+
if (!href) return trimmed;
|
|
1352
|
+
return `<a href="${_escapeHref(href)}" target="_blank" rel="noopener noreferrer">${trimmed}</a>`;
|
|
1353
|
+
});
|
|
1354
|
+
// 3.6. Auto-linkify bare https:// / http:// URLs not already inside href="...".
|
|
1355
|
+
// Sentinel prefix prevents double-linkifying empty-text [](url) patterns.
|
|
1356
|
+
s = s.replace(/(?<!href=")(?<!href=')(?<!\x00NOLINK\x00)(https?:\/\/[^\s<>"'\x00]+)/g, (url) => {
|
|
1357
|
+
const href = _safeHref(url);
|
|
1358
|
+
if (!href) return url;
|
|
1359
|
+
return `<a href="${_escapeHref(href)}" target="_blank" rel="noopener noreferrer">${_escapeHref(href)}</a>`;
|
|
1360
|
+
});
|
|
1361
|
+
// Strip sentinels: emit the URL as plain text (no anchor).
|
|
1362
|
+
s = s.replace(/\x00NOLINK\x00(.*?)\x00ENDNOLINK\x00/g, (_, url) => url);
|
|
1329
1363
|
// 4. Bold (**text**).
|
|
1330
1364
|
s = s.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
1331
1365
|
// 5–7. Process line by line for lists and paragraphs.
|
package/public/ai-hub/styles.css
CHANGED
|
@@ -1669,6 +1669,15 @@ img.coach-employee-avatar { object-fit: cover; border-radius: 4px; }
|
|
|
1669
1669
|
border: 1px solid var(--line);
|
|
1670
1670
|
color: var(--text);
|
|
1671
1671
|
}
|
|
1672
|
+
.message.employee .bubble a {
|
|
1673
|
+
color: var(--accent);
|
|
1674
|
+
text-decoration: underline;
|
|
1675
|
+
text-underline-offset: 2px;
|
|
1676
|
+
word-break: break-all;
|
|
1677
|
+
}
|
|
1678
|
+
.message.employee .bubble a:hover {
|
|
1679
|
+
color: var(--accent-strong);
|
|
1680
|
+
}
|
|
1672
1681
|
.typing-indicator {
|
|
1673
1682
|
animation: none;
|
|
1674
1683
|
justify-items: start;
|