[codex] Fix profile-aware session deep links (#962)

* feat: add session deep links for chats

* feat: add deep links for history and group chat

* Fix profile-aware session deep links

---------

Co-authored-by: Maxim Kirilyuk <werserk@inbox.ru>
This commit is contained in:
ekko
2026-05-24 10:55:55 +08:00
committed by GitHub
parent 8d261c3fa6
commit df41d6b051
22 changed files with 871 additions and 63 deletions
@@ -16,6 +16,7 @@ import {
type DropdownOption,
} from "naive-ui";
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { copyToClipboard } from "@/utils/clipboard";
import FolderPicker from "./FolderPicker.vue";
@@ -30,6 +31,7 @@ const chatStore = useChatStore();
const appStore = useAppStore();
const profilesStore = useProfilesStore();
const sessionBrowserPrefsStore = useSessionBrowserPrefsStore();
const router = useRouter();
const message = useMessage();
const { t } = useI18n();
@@ -58,8 +60,13 @@ const showSessions = ref(
let mobileQuery: MediaQueryList | null = null;
const isMobile = ref(false);
function handleSessionClick(sessionId: string) {
chatStore.switchSession(sessionId);
async function handleSessionClick(sessionId: string) {
const session = chatStore.sessions.find((item) => item.id === sessionId);
await router.push({
name: "hermes.session",
params: { sessionId },
query: session?.profile ? { profile: session.profile } : undefined,
});
if (mobileQuery?.matches) showSessions.value = false;
}
@@ -242,12 +249,17 @@ function handleNewChatProviderChange(value: string) {
newChatModel.value = newChatModelOptions.value[0]?.value || "";
}
function confirmNewChat() {
chatStore.newChat({
async function confirmNewChat() {
const session = chatStore.newChat({
profile: newChatProfile.value,
provider: newChatProvider.value,
model: newChatModel.value,
});
await router.push({
name: "hermes.session",
params: { sessionId: session.id },
query: session.profile ? { profile: session.profile } : undefined,
});
showNewChatModal.value = false;
}
@@ -255,6 +267,28 @@ function handleApproval(choice: "once" | "session" | "always" | "deny") {
chatStore.respondApproval(choice);
}
function sessionProfile(sessionId: string): string | null {
return chatStore.sessions.find((session) => session.id === sessionId)?.profile || null;
}
function buildSessionUrl(sessionId: string, profile?: string | null): string {
const href = router.resolve({
name: "hermes.session",
params: { sessionId },
query: profile ? { profile } : undefined,
}).href;
return `${window.location.origin}${window.location.pathname}${href}`;
}
async function copySessionLink(id?: string) {
const sessionId = id || chatStore.activeSessionId;
if (sessionId) {
const ok = await copyToClipboard(buildSessionUrl(sessionId, sessionProfile(sessionId)));
if (ok) message.success(t("common.copied"));
else message.error(t("common.copied") + " ✗");
}
}
async function copySessionId(id?: string) {
const sessionId = id || chatStore.activeSessionId;
if (sessionId) {
@@ -397,6 +431,7 @@ const contextMenuOptions = computed(() => {
},
],
})
options.push({ label: t("chat.copySessionLink"), key: "copy-link" })
options.push({ label: t("chat.copySessionId"), key: "copy-id" })
return options
});
@@ -428,7 +463,9 @@ async function handleContextMenuSelect(key: string) {
sessionBrowserPrefsStore.togglePinned(contextSessionId.value);
return;
}
if (key === "copy-id") {
if (key === "copy-link") {
copySessionLink(contextSessionId.value);
} else if (key === "copy-id") {
copySessionId(contextSessionId.value);
} else if (parseExportKey(key)) {
const { mode, ext } = parseExportKey(key)!;