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_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 =
|
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) {
|
function buildWorkspaceLink(path: string) {
|
||||||
return `${WORKSPACE_LINK_PREFIX}${encodeURIComponent(path)}`;
|
return `${WORKSPACE_LINK_PREFIX}${encodeURIComponent(path)}`;
|
||||||
|
|
@ -415,28 +416,55 @@ function parseWorkspaceLink(href: string): string | null {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decorateWorkspacePathsForMarkdown(text: string) {
|
function decorateWorkspacePathsInPlainChunk(source: string): string {
|
||||||
const source = String(text || '');
|
if (!source) return source;
|
||||||
const normalizedExistingLinks = source.replace(
|
const protectedLinks: string[] = [];
|
||||||
/\[(\/root\/\.nanobot\/workspace\/[^\]]+)\]\s*\n?\s*\((https:\/\/workspace\.local\/open\/[^)\r\n]*)\)/gi,
|
const withProtectedAbsoluteLinks = source.replace(WORKSPACE_ABS_PATH_PATTERN, (fullPath) => {
|
||||||
(_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) => {
|
|
||||||
const normalized = normalizeDashboardAttachmentPath(fullPath);
|
const normalized = normalizeDashboardAttachmentPath(fullPath);
|
||||||
if (!normalized) return 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 withRelativeLinks = withProtectedAbsoluteLinks.replace(
|
||||||
const normalized = normalizeDashboardAttachmentPath(rawPath);
|
WORKSPACE_RELATIVE_PATH_PATTERN,
|
||||||
if (!normalized) return full;
|
(full, prefix: string, rawPath: string) => {
|
||||||
return `${prefix}[${rawPath}](${buildWorkspaceLink(normalized)})`;
|
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[] {
|
function normalizeAttachmentPaths(raw: unknown): string[] {
|
||||||
if (!Array.isArray(raw)) return [];
|
if (!Array.isArray(raw)) return [];
|
||||||
return raw
|
return raw
|
||||||
|
|
@ -3117,6 +3145,21 @@ export function BotDashboardModule({
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : 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">
|
<div className="ops-composer">
|
||||||
<input
|
<input
|
||||||
ref={filePickerRef}
|
ref={filePickerRef}
|
||||||
|
|
@ -3179,21 +3222,6 @@ export function BotDashboardModule({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 ? (
|
{!canChat ? (
|
||||||
<div className="ops-chat-disabled-mask">
|
<div className="ops-chat-disabled-mask">
|
||||||
<div className="ops-chat-disabled-card">
|
<div className="ops-chat-disabled-card">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue