mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
* ✨ feat(electron): add + button to TabBar to open new topic in active context Introduce a pluggable `createNewTabAction` extension on RecentlyViewed plugins so each page type can decide whether (and how) to spawn a new tab from the active tab. Implemented for agent / agent-topic / group / group-topic — clicking `+` creates a fresh topic under the current agent/group and opens it as a new tab; other page types hide the button by default. * ✨ feat(electron): support new tab from page context Page plugin now implements `createNewTabAction`, creating a fresh untitled document via `usePageStore().createPage` and opening it as a new `page` tab. * 🐛 fix(electron): refresh page list after creating a new page via TabBar + `createPage` only hits the service; without refreshing the documents list, the sidebar / PageExplorer wouldn't show the freshly-created page until the next full reload. * 🐛 fix(electron): highlight new page in sidebar when opened via TabBar + Switch to `createNewPage`, which runs the full optimistic flow — dispatches the new document into the sidebar list and sets `selectedPageId` — so the nav item active state stays in sync with the freshly-opened page tab. * 🐛 fix(electron): dispatch real page doc into sidebar list for TabBar + The earlier `createNewPage` approach relied on an optimistic temp document that SWR revalidation can clobber before the real doc replaces it, leaving the new page absent from the sidebar. Create the page via `createPage` first, then synthesize a `LobeDocument` from the server response and dispatch it into the list alongside setting `selectedPageId` — the nav item now appears and highlights in sync with the new tab.
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { MessageSquare } from 'lucide-react';
|
|
|
|
import { useChatStore } from '@/store/chat';
|
|
|
|
import { type AgentTopicParams, type PageReference, type ResolvedPageData } from '../types';
|
|
import { buildAgentNewTopicAction } from './newTabHelpers';
|
|
import { type NewTabAction, type PluginContext, type RecentlyViewedPlugin } from './types';
|
|
import { createPageReference } from './types';
|
|
|
|
const AGENT_PATH_REGEX = /^\/agent\/([^/?]+)$/;
|
|
|
|
export const agentTopicPlugin: RecentlyViewedPlugin<'agent-topic'> = {
|
|
checkExists(reference: PageReference<'agent-topic'>, ctx: PluginContext): boolean {
|
|
const { agentId, topicId } = reference.params;
|
|
const agentMeta = ctx.getAgentMeta(agentId);
|
|
const topic = ctx.getTopic(topicId);
|
|
|
|
// Both agent and topic must exist
|
|
return agentMeta !== undefined && Object.keys(agentMeta).length > 0 && topic !== undefined;
|
|
},
|
|
|
|
createNewTabAction(
|
|
reference: PageReference<'agent-topic'>,
|
|
ctx: PluginContext,
|
|
): NewTabAction | null {
|
|
return buildAgentNewTopicAction(reference.params.agentId, ctx);
|
|
},
|
|
|
|
generateId(reference: PageReference<'agent-topic'>): string {
|
|
const { agentId, topicId } = reference.params;
|
|
return `agent-topic:${agentId}:${topicId}`;
|
|
},
|
|
|
|
generateUrl(reference: PageReference<'agent-topic'>): string {
|
|
const { agentId, topicId } = reference.params;
|
|
return `/agent/${agentId}?topic=${topicId}`;
|
|
},
|
|
|
|
getDefaultIcon() {
|
|
return MessageSquare;
|
|
},
|
|
|
|
// Higher priority than agent plugin to match topic URLs first
|
|
matchUrl(pathname: string, searchParams: URLSearchParams): boolean {
|
|
// Match /agent/:id with topic param
|
|
return AGENT_PATH_REGEX.test(pathname) && searchParams.has('topic');
|
|
},
|
|
|
|
onActivate(reference: PageReference<'agent-topic'>) {
|
|
useChatStore.getState().switchTopic(reference.params.topicId);
|
|
},
|
|
|
|
parseUrl(pathname: string, searchParams: URLSearchParams): PageReference<'agent-topic'> | null {
|
|
const match = pathname.match(AGENT_PATH_REGEX);
|
|
if (!match) return null;
|
|
|
|
const topicId = searchParams.get('topic');
|
|
if (!topicId) return null;
|
|
|
|
const agentId = match[1];
|
|
const params: AgentTopicParams = { agentId, topicId };
|
|
const id = this.generateId({ params } as PageReference<'agent-topic'>);
|
|
|
|
return createPageReference('agent-topic', params, id);
|
|
},
|
|
|
|
priority: 20,
|
|
|
|
resolve(reference: PageReference<'agent-topic'>, ctx: PluginContext): ResolvedPageData {
|
|
const { agentId, topicId } = reference.params;
|
|
const agentMeta = ctx.getAgentMeta(agentId);
|
|
const topic = ctx.getTopic(topicId);
|
|
|
|
const cached = reference.cached;
|
|
|
|
const agentExists = agentMeta !== undefined && Object.keys(agentMeta).length > 0;
|
|
const topicExists = topic !== undefined;
|
|
const hasStoreData = agentExists && topicExists;
|
|
|
|
// Use topic title if available, otherwise fall back to agent title, then cached
|
|
const title =
|
|
topic?.title ||
|
|
agentMeta?.title ||
|
|
cached?.title ||
|
|
ctx.t('navigation.chat', { ns: 'electron' });
|
|
|
|
return {
|
|
avatar: agentMeta?.avatar ?? cached?.avatar,
|
|
backgroundColor: agentMeta?.backgroundColor ?? cached?.backgroundColor,
|
|
exists: hasStoreData || cached !== undefined,
|
|
icon: this.getDefaultIcon!(),
|
|
reference,
|
|
title,
|
|
url: this.generateUrl(reference),
|
|
};
|
|
},
|
|
|
|
type: 'agent-topic',
|
|
};
|