feat(mindspace): 0630004 空间 UI、聊天连接、微信分享与 Agent 能力

含 MindSpace 三列布局与统计修复、聊天加载态与连接降级、平台页脚标记与 og:site_name 微信卡片、勾选资料删除 Agent 接口及内部话术过滤。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-06-30 09:30:51 +08:00
parent b1b8d3afc6
commit 722b18326f
53 changed files with 2450 additions and 313 deletions
+81 -11
View File
@@ -34,6 +34,57 @@ export function resolveWorkspaceRelativeFilePath(htmlRelativePath, relativeRef)
return path.posix.join(htmlDir, ref).replace(/\\/g, '/');
}
export function resolveSiblingDocxPath(htmlRelativePath) {
const htmlPath = String(htmlRelativePath ?? '').replace(/^\/+/, '');
if (!htmlPath.toLowerCase().endsWith('.html')) return null;
return htmlPath.replace(/\.html$/i, '.docx');
}
function resolveExistingSiblingDocxPath(htmlRelativePath, publishDir) {
const sibling = resolveSiblingDocxPath(htmlRelativePath);
if (!sibling || !publishDir) return null;
return workspaceFileExists(publishDir, sibling) ? sibling : null;
}
export function inferWorkspaceHtmlRelativePath({
snapshotRelativePath = null,
pageTitle = null,
htmlContent = null,
publishDir = null,
} = {}) {
const fromSnapshot = String(snapshotRelativePath ?? '').trim();
if (fromSnapshot) {
const normalized = fromSnapshot.replace(/^\/+/, '');
if (normalized.toLowerCase().startsWith('public/')) return normalized;
if (normalized.toLowerCase().endsWith('.html')) return `public/${path.posix.basename(normalized)}`;
return normalized;
}
const publicDir = publishDir ? path.join(publishDir, 'public') : null;
if (!publicDir || !fs.existsSync(publicDir) || !htmlContent) {
return 'public/index.html';
}
const normalizedContent = String(htmlContent);
const title = String(pageTitle ?? '').trim();
for (const name of fs.readdirSync(publicDir)) {
if (!name.toLowerCase().endsWith('.html')) continue;
const absolutePath = path.join(publicDir, name);
try {
const diskContent = fs.readFileSync(absolutePath, 'utf8');
if (diskContent === normalizedContent) return `public/${name}`;
if (title) {
const titleMatch = diskContent.match(/<title[^>]*>([^<]+)<\/title>/i);
if (titleMatch?.[1]?.trim() === title) return `public/${name}`;
}
} catch {
// ignore unreadable files
}
}
return 'public/index.html';
}
export function buildWorkspaceDownloadLinkIndex(assets = []) {
const byBasename = new Map();
const byPath = new Map();
@@ -45,6 +96,8 @@ export function buildWorkspaceDownloadLinkIndex(assets = []) {
byBasename.set(path.posix.basename(filename), id);
if (filename.startsWith('public/')) {
byPath.set(filename.slice('public/'.length), id);
} else if (!filename.includes('/')) {
byPath.set(`public/${filename}`, id);
}
}
return { byBasename, byPath };
@@ -80,6 +133,20 @@ function workspaceFileExists(publishDir, workspaceRelativePath) {
}
}
function collectDownloadWorkspaceCandidates(htmlRelativePath, relativeRef, publishDir) {
const workspacePath = resolveWorkspaceRelativeFilePath(htmlRelativePath, relativeRef);
const candidates = new Set([
workspacePath,
path.posix.join('public', path.posix.basename(workspacePath)),
]);
const siblingDocx = resolveSiblingDocxPath(htmlRelativePath);
if (siblingDocx) {
candidates.add(siblingDocx);
candidates.add(path.posix.join('public', path.posix.basename(siblingDocx)));
}
return [...candidates];
}
export function resolveDownloadTargetUrl({
relativeRef,
htmlRelativePath = 'public/index.html',
@@ -90,27 +157,28 @@ export function resolveDownloadTargetUrl({
preferAssetDownload = true,
}) {
const { suffix } = splitReferenceParts(relativeRef);
const workspacePath = resolveWorkspaceRelativeFilePath(htmlRelativePath, relativeRef);
const assetId = lookupAssetIdForWorkspacePath(linkIndex, workspacePath);
const publicCandidates = [
workspacePath,
path.posix.join('public', path.posix.basename(workspacePath)),
];
const candidates = collectDownloadWorkspaceCandidates(htmlRelativePath, relativeRef, publishDir);
if (preferAssetDownload && assetId) {
return `${buildAssetDownloadUrl(assetId)}${suffix}`;
for (const candidate of candidates) {
const assetId = lookupAssetIdForWorkspacePath(linkIndex, candidate);
if (preferAssetDownload && assetId) {
return `${buildAssetDownloadUrl(assetId)}${suffix}`;
}
}
if (publishDir && publishKey) {
for (const candidate of publicCandidates) {
for (const candidate of candidates) {
if (workspaceFileExists(publishDir, candidate)) {
return `${buildPublicWorkspaceFileUrl(publicBaseUrl, publishKey, candidate)}${suffix}`;
}
}
}
if (assetId) {
return `${buildAssetDownloadUrl(assetId)}${suffix}`;
for (const candidate of candidates) {
const assetId = lookupAssetIdForWorkspacePath(linkIndex, candidate);
if (assetId) {
return `${buildAssetDownloadUrl(assetId)}${suffix}`;
}
}
return null;
@@ -150,6 +218,8 @@ export async function prepareHtmlDownloadLinks(pool, userId, html, options = {})
export const downloadLinkInternals = {
isRelativeDownloadReference,
resolveWorkspaceRelativeFilePath,
resolveSiblingDocxPath,
inferWorkspaceHtmlRelativePath,
buildWorkspaceDownloadLinkIndex,
lookupAssetIdForWorkspacePath,
resolveDownloadTargetUrl,