v0.1.4
parent
76a2ab49fe
commit
b0e8cd6514
|
|
@ -395,9 +395,10 @@ function workspaceFileAction(path: string): 'preview' | 'download' | 'unsupporte
|
|||
}
|
||||
|
||||
const WORKSPACE_LINK_PREFIX = 'https://workspace.local/open/';
|
||||
const WORKSPACE_ABS_PATH_PATTERN = /\/root\/\.nanobot\/workspace\/[^\s<>"'`)\],,。!?;:]+/gi;
|
||||
const WORKSPACE_ABS_PATH_PATTERN =
|
||||
/\/root\/\.nanobot\/workspace\/[^\n\r<>"'`]+?\.(?:md|markdown|json|txt|log|csv|tsv|yaml|yml|toml|html|htm|pdf|png|jpg|jpeg|gif|webp|svg|mp3|wav|m4a|flac|ogg|opus|aac|amr|wma|mp4|mov|avi|mkv|webm|m4v|3gp|mpeg|mpg|ts|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps)\b/gi;
|
||||
const WORKSPACE_RELATIVE_PATH_PATTERN =
|
||||
/(^|[\s(\[])(\/[^\s<>"'`)\]]+\.(?:md|markdown|json|txt|log|csv|tsv|yaml|yml|toml|html|htm|pdf|png|jpg|jpeg|gif|webp|svg))(?![A-Za-z0-9_./-])/gim;
|
||||
/(^|[\s(\[])(\/[^\n\r<>"'`)\]]+?\.(?:md|markdown|json|txt|log|csv|tsv|yaml|yml|toml|html|htm|pdf|png|jpg|jpeg|gif|webp|svg|mp3|wav|m4a|flac|ogg|opus|aac|amr|wma|mp4|mov|avi|mkv|webm|m4v|3gp|mpeg|mpg|ts|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps))(?![A-Za-z0-9_./-])/gim;
|
||||
|
||||
function buildWorkspaceLink(path: string) {
|
||||
return `${WORKSPACE_LINK_PREFIX}${encodeURIComponent(path)}`;
|
||||
|
|
@ -415,28 +416,55 @@ function parseWorkspaceLink(href: string): string | null {
|
|||
}
|
||||
}
|
||||
|
||||
function decorateWorkspacePathsForMarkdown(text: string) {
|
||||
const source = String(text || '');
|
||||
const normalizedExistingLinks = source.replace(
|
||||
/\[(\/root\/\.nanobot\/workspace\/[^\]]+)\]\s*\n?\s*\((https:\/\/workspace\.local\/open\/[^)\r\n]*)\)/gi,
|
||||
(_full, markdownPath: string) => {
|
||||
const normalized = normalizeDashboardAttachmentPath(markdownPath);
|
||||
if (!normalized) return String(_full || '');
|
||||
return `[${markdownPath}](${buildWorkspaceLink(normalized)})`;
|
||||
},
|
||||
);
|
||||
const withAbsoluteLinks = normalizedExistingLinks.replace(WORKSPACE_ABS_PATH_PATTERN, (fullPath) => {
|
||||
function decorateWorkspacePathsInPlainChunk(source: string): string {
|
||||
if (!source) return source;
|
||||
const protectedLinks: string[] = [];
|
||||
const withProtectedAbsoluteLinks = source.replace(WORKSPACE_ABS_PATH_PATTERN, (fullPath) => {
|
||||
const normalized = normalizeDashboardAttachmentPath(fullPath);
|
||||
if (!normalized) return fullPath;
|
||||
return `[${fullPath}](${buildWorkspaceLink(normalized)})`;
|
||||
const token = `@@WS_PATH_LINK_${protectedLinks.length}@@`;
|
||||
protectedLinks.push(`[${fullPath}](${buildWorkspaceLink(normalized)})`);
|
||||
return token;
|
||||
});
|
||||
return withAbsoluteLinks.replace(WORKSPACE_RELATIVE_PATH_PATTERN, (full, prefix: string, rawPath: string) => {
|
||||
const normalized = normalizeDashboardAttachmentPath(rawPath);
|
||||
if (!normalized) return full;
|
||||
return `${prefix}[${rawPath}](${buildWorkspaceLink(normalized)})`;
|
||||
const withRelativeLinks = withProtectedAbsoluteLinks.replace(
|
||||
WORKSPACE_RELATIVE_PATH_PATTERN,
|
||||
(full, prefix: string, rawPath: string) => {
|
||||
const normalized = normalizeDashboardAttachmentPath(rawPath);
|
||||
if (!normalized) return full;
|
||||
return `${prefix}[${rawPath}](${buildWorkspaceLink(normalized)})`;
|
||||
},
|
||||
);
|
||||
return withRelativeLinks.replace(/@@WS_PATH_LINK_(\d+)@@/g, (_full, idxRaw: string) => {
|
||||
const idx = Number(idxRaw);
|
||||
if (!Number.isFinite(idx) || idx < 0 || idx >= protectedLinks.length) return String(_full || '');
|
||||
return protectedLinks[idx];
|
||||
});
|
||||
}
|
||||
|
||||
function decorateWorkspacePathsForMarkdown(text: string) {
|
||||
const source = String(text || '');
|
||||
if (!source) return source;
|
||||
|
||||
// Keep existing Markdown links unchanged; only decorate plain text segments.
|
||||
const markdownLinkPattern = /\[[^\]]*?\]\((?:[^)(]|\([^)(]*\))*\)/g;
|
||||
let result = '';
|
||||
let last = 0;
|
||||
let match = markdownLinkPattern.exec(source);
|
||||
while (match) {
|
||||
const idx = Number(match.index || 0);
|
||||
if (idx > last) {
|
||||
result += decorateWorkspacePathsInPlainChunk(source.slice(last, idx));
|
||||
}
|
||||
result += match[0];
|
||||
last = idx + match[0].length;
|
||||
match = markdownLinkPattern.exec(source);
|
||||
}
|
||||
if (last < source.length) {
|
||||
result += decorateWorkspacePathsInPlainChunk(source.slice(last));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeAttachmentPaths(raw: unknown): string[] {
|
||||
if (!Array.isArray(raw)) return [];
|
||||
return raw
|
||||
|
|
@ -3117,6 +3145,21 @@ export function BotDashboardModule({
|
|||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{isUploadingAttachments ? (
|
||||
<div className="ops-upload-progress" aria-live="polite">
|
||||
<div className={`ops-upload-progress-track ${attachmentUploadPercent === null ? 'is-indeterminate' : ''}`}>
|
||||
<div
|
||||
className="ops-upload-progress-fill"
|
||||
style={{ width: `${Math.max(3, Number(attachmentUploadPercent ?? 24))}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="ops-upload-progress-text mono">
|
||||
{attachmentUploadPercent === null
|
||||
? t.uploadingFile
|
||||
: `${t.uploadingFile} ${attachmentUploadPercent}%`}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="ops-composer">
|
||||
<input
|
||||
ref={filePickerRef}
|
||||
|
|
@ -3179,21 +3222,6 @@ export function BotDashboardModule({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isUploadingAttachments ? (
|
||||
<div className="ops-upload-progress" aria-live="polite">
|
||||
<div className={`ops-upload-progress-track ${attachmentUploadPercent === null ? 'is-indeterminate' : ''}`}>
|
||||
<div
|
||||
className="ops-upload-progress-fill"
|
||||
style={{ width: `${Math.max(3, Number(attachmentUploadPercent ?? 24))}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="ops-upload-progress-text mono">
|
||||
{attachmentUploadPercent === null
|
||||
? t.uploadingFile
|
||||
: `${t.uploadingFile} ${attachmentUploadPercent}%`}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
{!canChat ? (
|
||||
<div className="ops-chat-disabled-mask">
|
||||
<div className="ops-chat-disabled-card">
|
||||
|
|
|
|||
Loading…
Reference in New Issue