feat(mindspace): 0630004 空间 UI、聊天连接、微信分享与 Agent 能力
含 MindSpace 三列布局与统计修复、聊天加载态与连接降级、平台页脚标记与 og:site_name 微信卡片、勾选资料删除 Agent 接口及内部话术过滤。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user