v0.1.4
parent
82ce7d7373
commit
76a2ab49fe
|
|
@ -452,6 +452,63 @@ function normalizeDashboardAttachmentPath(path: string): string {
|
|||
return v.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
const COMPOSER_DRAFT_STORAGE_PREFIX = 'nanobot-dashboard-composer-draft:v1:';
|
||||
|
||||
interface ComposerDraftStorage {
|
||||
command: string;
|
||||
attachments: string[];
|
||||
updated_at_ms: number;
|
||||
}
|
||||
|
||||
function getComposerDraftStorageKey(botId: string): string {
|
||||
return `${COMPOSER_DRAFT_STORAGE_PREFIX}${String(botId || '').trim()}`;
|
||||
}
|
||||
|
||||
function loadComposerDraft(botId: string): ComposerDraftStorage | null {
|
||||
const id = String(botId || '').trim();
|
||||
if (!id || typeof window === 'undefined') return null;
|
||||
try {
|
||||
const raw = window.localStorage.getItem(getComposerDraftStorageKey(id));
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as Partial<ComposerDraftStorage> | null;
|
||||
const command = String(parsed?.command || '');
|
||||
const attachments = normalizeAttachmentPaths(parsed?.attachments)
|
||||
.map(normalizeDashboardAttachmentPath)
|
||||
.filter(Boolean);
|
||||
return {
|
||||
command,
|
||||
attachments,
|
||||
updated_at_ms: Number(parsed?.updated_at_ms || Date.now()),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function persistComposerDraft(botId: string, commandRaw: string, attachmentsRaw: string[]): void {
|
||||
const id = String(botId || '').trim();
|
||||
if (!id || typeof window === 'undefined') return;
|
||||
const command = String(commandRaw || '');
|
||||
const attachments = normalizeAttachmentPaths(attachmentsRaw)
|
||||
.map(normalizeDashboardAttachmentPath)
|
||||
.filter(Boolean);
|
||||
const key = getComposerDraftStorageKey(id);
|
||||
try {
|
||||
if (!command.trim() && attachments.length === 0) {
|
||||
window.localStorage.removeItem(key);
|
||||
return;
|
||||
}
|
||||
const payload: ComposerDraftStorage = {
|
||||
command,
|
||||
attachments,
|
||||
updated_at_ms: Date.now(),
|
||||
};
|
||||
window.localStorage.setItem(key, JSON.stringify(payload));
|
||||
} catch {
|
||||
// ignore localStorage write failures
|
||||
}
|
||||
}
|
||||
|
||||
function isExternalHttpLink(href: string): boolean {
|
||||
return /^https?:\/\//i.test(String(href || '').trim());
|
||||
}
|
||||
|
|
@ -631,6 +688,7 @@ export function BotDashboardModule({
|
|||
const [workspaceAutoRefresh, setWorkspaceAutoRefresh] = useState(false);
|
||||
const [workspaceQuery, setWorkspaceQuery] = useState('');
|
||||
const [pendingAttachments, setPendingAttachments] = useState<string[]>([]);
|
||||
const [composerDraftHydrated, setComposerDraftHydrated] = useState(false);
|
||||
const [quotedReply, setQuotedReply] = useState<QuotedReply | null>(null);
|
||||
const [isUploadingAttachments, setIsUploadingAttachments] = useState(false);
|
||||
const [attachmentUploadPercent, setAttachmentUploadPercent] = useState<number | null>(null);
|
||||
|
|
@ -700,7 +758,6 @@ export function BotDashboardModule({
|
|||
memory_mb: String(clampMemoryMb(bot.memory_mb ?? 1024)),
|
||||
storage_gb: String(clampStorageGb(bot.storage_gb ?? 10)),
|
||||
});
|
||||
setPendingAttachments([]);
|
||||
}, []);
|
||||
const buildWorkspaceDownloadHref = (filePath: string, forceDownload: boolean = true) => {
|
||||
const query = [`path=${encodeURIComponent(filePath)}`];
|
||||
|
|
@ -1383,6 +1440,36 @@ export function BotDashboardModule({
|
|||
if (selectedBotId && !activeBots[selectedBotId] && bots.length > 0) setSelectedBotId(bots[0].id);
|
||||
}, [bots, selectedBotId, activeBots, forcedBotId]);
|
||||
|
||||
useEffect(() => {
|
||||
setComposerDraftHydrated(false);
|
||||
if (!selectedBotId) {
|
||||
setCommand('');
|
||||
setPendingAttachments([]);
|
||||
setComposerDraftHydrated(true);
|
||||
return;
|
||||
}
|
||||
const draft = loadComposerDraft(selectedBotId);
|
||||
setCommand(draft?.command || '');
|
||||
setPendingAttachments(draft?.attachments || []);
|
||||
setComposerDraftHydrated(true);
|
||||
}, [selectedBotId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedBotId || !composerDraftHydrated) return;
|
||||
persistComposerDraft(selectedBotId, command, pendingAttachments);
|
||||
}, [selectedBotId, composerDraftHydrated, command, pendingAttachments]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasDraft = Boolean(String(command || '').trim()) || pendingAttachments.length > 0 || Boolean(quotedReply);
|
||||
if (!hasDraft && !isUploadingAttachments) return;
|
||||
const onBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
};
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
return () => window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
}, [command, pendingAttachments.length, quotedReply, isUploadingAttachments]);
|
||||
|
||||
useEffect(() => {
|
||||
chatBottomRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
}, [selectedBotId, conversation.length]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue