|
<script setup lang="ts">
|
|
import { onMounted, ref, computed, h } from 'vue';
|
|
import { NEmpty, NButton, useDialog, useMessage, NResult, NInput, NAlert, NModal, NPopover, NVirtualList} from 'naive-ui';
|
|
import conversationCssText from '@/assets/css/conversation.css?raw';
|
|
import { usePromptStore, type IPrompt } from '@/stores/modules/prompt';
|
|
import { storeToRefs } from 'pinia';
|
|
import ChatPromptItem from './ChatPromptItem.vue';
|
|
import { isMobile } from '@/utils/utils';
|
|
import cookies from '@/utils/cookies';
|
|
import LoadingSpinner from '@/components/LoadingSpinner/LoadingSpinner.vue';
|
|
import { ApiResultCode } from '@/api/model/ApiResult';
|
|
import type { SysConfig } from '@/api/model/sysconf/SysConfig';
|
|
import { useChatStore } from '@/stores/modules/chat';
|
|
import ChatServiceSelect from '@/components/ChatServiceSelect/ChatServiceSelect.vue';
|
|
import { useUserStore } from '@/stores/modules/user';
|
|
|
|
const message = useMessage();
|
|
const dialog = useDialog();
|
|
(window as any).$dialog = dialog;
|
|
|
|
const isShowLoading = ref(true);
|
|
|
|
const promptStore = usePromptStore();
|
|
const { isShowPromptSotre, isShowChatPrompt, keyword, promptList, searchPromptList, selectedPromptIndex } = storeToRefs(promptStore);
|
|
|
|
const chatStore = useChatStore();
|
|
const { isShowChatServiceSelectModal, sydneyConfigs, selectedSydneyBaseUrl } = storeToRefs(chatStore);
|
|
|
|
const userStore = useUserStore();
|
|
|
|
const scrollbarRef = ref<{
|
|
scrollToIndex: (index: number) => {};
|
|
getOffset: () => number;
|
|
getClientSize: () => number;
|
|
getScrollSize: () => number;
|
|
}>();
|
|
const isInput = ref(false);
|
|
const isPromptScrolling = ref(false);
|
|
const promptItemHeight = 130;
|
|
|
|
const isShowUnauthorizedModal = ref(false);
|
|
const authKey = ref('');
|
|
const isAuthBtnLoading = ref(false);
|
|
|
|
const isShowHistory = computed(() => {
|
|
return (CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleMobile) || (!CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleDesktop);
|
|
});
|
|
|
|
const { themeMode, uiVersion, gpt4tEnable, sydneyEnable, sydneyPrompt, enterpriseEnable, copilotProEnable } = storeToRefs(userStore);
|
|
|
|
onMounted(async () => {
|
|
await initChat();
|
|
hackDevMode();
|
|
|
|
|
|
await SydneyFullScreenConv.initWithWaitlistUpdate({ cookLoc: {} }, 10);
|
|
if (isMobile()) {
|
|
const serpEle = document.querySelector('cib-serp');
|
|
serpEle?.setAttribute('mobile', '');
|
|
}
|
|
if (uiVersion.value === 'v3') {
|
|
await sj_evt.bind('chs_init', () => {
|
|
ChatHomeScreen.init('/turing/api/suggestions/v2/zeroinputstarter');
|
|
}, true);
|
|
}
|
|
initSysConfig();
|
|
|
|
isShowLoading.value = false;
|
|
hackStyle();
|
|
hackEnterprise();
|
|
initSydney();
|
|
initChatPrompt();
|
|
|
|
|
|
if (themeMode.value == 'light') {
|
|
CIB.changeColorScheme(0);
|
|
} else if (themeMode.value == 'dark') {
|
|
CIB.changeColorScheme(1);
|
|
} else if (themeMode.value == 'auto') {
|
|
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
CIB.changeColorScheme(1);
|
|
} else {
|
|
CIB.changeColorScheme(0);
|
|
}
|
|
}
|
|
});
|
|
|
|
const sleep = async (ms: number) => {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
const hackDevMode = () => {
|
|
if (import.meta.env.DEV) {
|
|
CIB.manager.chat.api.bing._endpoint = location.origin;
|
|
}
|
|
};
|
|
|
|
const initChatService = () => {
|
|
if (selectedSydneyBaseUrl.value) {
|
|
CIB.config.sydney.baseUrl = selectedSydneyBaseUrl.value;
|
|
isShowChatServiceSelectModal.value = false;
|
|
} else {
|
|
isShowChatServiceSelectModal.value = true;
|
|
selectedSydneyBaseUrl.value = CIB.config.sydney.baseUrl;
|
|
const isCus = sydneyConfigs.value.filter((x) => !x.isCus).every((x) => x.baseUrl !== selectedSydneyBaseUrl.value);
|
|
if (isCus) {
|
|
const cusSydneyConfig = sydneyConfigs.value.find((x) => x.isCus);
|
|
if (cusSydneyConfig) {
|
|
cusSydneyConfig.baseUrl = selectedSydneyBaseUrl.value;
|
|
}
|
|
}
|
|
chatStore.checkAllSydneyConfig();
|
|
}
|
|
};
|
|
|
|
const initSysConfig = async () => {
|
|
const S = base58Decode(_G.S);
|
|
let tmpA = [];
|
|
for (let i = 0; i < _G.SP.length; i++) {
|
|
tmpA.push(S[_G.SP[i]]);
|
|
}
|
|
const token = base58Decode(tmpA.join(''));
|
|
if (token != _G.AT) {
|
|
dialog.warning({
|
|
title: decodeURI(base58Decode(_G.TIP)),
|
|
content: decodeURI(base58Decode(_G.TIPC)),
|
|
maskClosable: false,
|
|
closable: false,
|
|
closeOnEsc: false,
|
|
});
|
|
}
|
|
const res = await userStore.getSysConfig();
|
|
switch (res.code) {
|
|
case ApiResultCode.OK:
|
|
{
|
|
if (!res.data.isAuth) {
|
|
isShowUnauthorizedModal.value = true;
|
|
return;
|
|
}
|
|
await afterAuth(res.data);
|
|
let MATD_Cookie = cookies.get('MicrosoftApplicationsTelemetryDeviceId');
|
|
if (MATD_Cookie == '' || MATD_Cookie == null) {
|
|
MATD_Cookie = crypto.randomUUID();
|
|
cookies.set('MicrosoftApplicationsTelemetryDeviceId', MATD_Cookie, 60, '/');
|
|
}
|
|
let RWBF_Cookie = userStore.getUserRwBf();
|
|
if (RWBF_Cookie != '') {
|
|
let RWBFs = RWBF_Cookie.split('&');
|
|
for (let i = 0; i < RWBFs.length; i++) {
|
|
if (RWBFs[i].startsWith('wls=')) {
|
|
RWBFs[i] = 'wls=2';
|
|
}
|
|
}
|
|
RWBF_Cookie = RWBFs.join('&');
|
|
userStore.saveUserRwBf(RWBF_Cookie);
|
|
}
|
|
if (res.data.info != '') {
|
|
const info = JSON.parse(res.data.info);
|
|
message.create(info['content'], {
|
|
type: info['type'],
|
|
keepAliveOnHover: true,
|
|
showIcon: true,
|
|
render: (props) => {
|
|
return h(
|
|
NAlert,
|
|
{
|
|
closable: true,
|
|
type: props.type === 'loading' ? 'default' : props.type,
|
|
title: info['title'],
|
|
style: {
|
|
boxShadow: 'var(--n-box-shadow)',
|
|
maxWidth: 'calc(100vw - 32px)',
|
|
width: '360px',
|
|
position: 'fixed',
|
|
top: '20px',
|
|
right: '12px',
|
|
}
|
|
},
|
|
{
|
|
default: () => props.content
|
|
}
|
|
)
|
|
}
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
case ApiResultCode.UnLegal:
|
|
{
|
|
_G.SB = true
|
|
dialog.warning({
|
|
title: decodeURI(base58Decode(_G.TIP)),
|
|
content: decodeURI(base58Decode(_G.TIPC)),
|
|
maskClosable: false,
|
|
closable: false,
|
|
closeOnEsc: false,
|
|
});
|
|
}
|
|
break;
|
|
default:
|
|
message.error(`[${res.code}] ${res.message}`);
|
|
break;
|
|
}
|
|
};
|
|
|
|
const afterAuth = async (data: SysConfig) => {
|
|
if (!data.isSysCK) {
|
|
await userStore.checkUserToken();
|
|
}
|
|
initChatService();
|
|
};
|
|
|
|
const initChat = async () => {
|
|
return new Promise((resolve, reject) => {
|
|
sj_evt.bind('sydFSC.init', resolve, true);
|
|
sj_evt.fire('showSydFSC');
|
|
});
|
|
};
|
|
|
|
const hackStyle = async() => {
|
|
if (location.hostname === 'localhost') {
|
|
CIB.config.sydney.hostnamesToBypassSecureConnection = CIB.config.sydney.hostnamesToBypassSecureConnection.filter((x) => x !== location.hostname);
|
|
}
|
|
if (isMobile()) {
|
|
await sleep(25);
|
|
}
|
|
const serpEle = document.querySelector('cib-serp');
|
|
const conversationEle = serpEle?.shadowRoot?.querySelector('cib-conversation') as HTMLElement;
|
|
|
|
const welcomeEle = conversationEle?.shadowRoot?.querySelector('cib-welcome-container');
|
|
const loginTip = welcomeEle?.shadowRoot?.querySelectorAll("div[class='muid-upsell']");
|
|
if (loginTip?.length) {
|
|
loginTip.forEach((ele) => {
|
|
ele.remove();
|
|
});
|
|
}
|
|
welcomeEle?.shadowRoot?.querySelector('.preview-container')?.remove();
|
|
welcomeEle?.shadowRoot?.querySelector('.footer')?.remove();
|
|
|
|
serpEle?.shadowRoot?.querySelector('cib-serp-feedback')?.remove();
|
|
if (isMobile()) {
|
|
welcomeEle?.shadowRoot?.querySelector('.container-item')?.remove();
|
|
CIB.vm.actionBar.input.placeholder = '有问题尽管问我...("/" 触发提示词)';
|
|
}
|
|
|
|
const conversationStyleEle = document.createElement('style');
|
|
conversationStyleEle.innerText = conversationCssText;
|
|
conversationEle.shadowRoot?.append(conversationStyleEle);
|
|
};
|
|
|
|
interface IActionBarElement extends HTMLElement {
|
|
handleInputTextKey: (ev: KeyboardEvent) => void;
|
|
}
|
|
|
|
const hackEnterprise = () => {
|
|
if (enterpriseEnable.value) {
|
|
CIB.config.bingAtWork.isBingChatForEnterpriseEnabled = true;
|
|
CIB.config.bingAtWork.chatType = "enterprise";
|
|
}
|
|
}
|
|
|
|
const initSydney = () => {
|
|
if (copilotProEnable.value) {
|
|
hackCopilotPro();
|
|
}
|
|
if (gpt4tEnable.value) {
|
|
hackG4t();
|
|
}
|
|
if (sydneyEnable.value) {
|
|
hackSydney();
|
|
|
|
CIB.manager.resetConversation = function (O, B=!0, U=!0) {
|
|
var G;
|
|
null === (G = CIB.manager.log) || void 0 === G || G.trace(this, CIB.manager.resetConversation, null).write(),
|
|
CIB.manager.chat.cancelPendingRequest(),
|
|
CIB.manager.finalizeResetConversation(O, B, U);
|
|
hackSydney(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
const hackCopilotPro = () => {
|
|
CIB.config.sydney.request.source = 'cib-ccp'
|
|
}
|
|
|
|
const hackG4t = () => {
|
|
CIB.config.sydney.request.optionsSets.push(
|
|
"dlgpt4t",
|
|
"dlbmtc",
|
|
"dlbpc4575",
|
|
"dlbrngnp",
|
|
"dlbtc",
|
|
"dlbuc07",
|
|
"dlbuf03"
|
|
)
|
|
}
|
|
|
|
const hackSydney = (first=true) => {
|
|
if (first == true) {
|
|
CIB.config.sydney.request.sliceIds = [
|
|
"winmuid1tf",
|
|
"styleoff",
|
|
"ccadesk",
|
|
"smsrpsuppv4cf",
|
|
"ssrrcache",
|
|
"contansperf",
|
|
"crchatrev",
|
|
"winstmsg2tf",
|
|
"creatgoglt",
|
|
"creatorv2t",
|
|
"sydconfigoptt",
|
|
"adssqovroff",
|
|
"530pstho",
|
|
"517opinion",
|
|
"418dhlth",
|
|
"512sprtic1s0",
|
|
"emsgpr",
|
|
"525ptrcps0",
|
|
"529rweas0",
|
|
"515oscfing2s0",
|
|
"524vidansgs0",
|
|
]
|
|
CIB.config.sydney.request.optionsSets.push("rai278", "nojbfedge")
|
|
if (uiVersion.value != 'v3') {
|
|
CIB.config.features.enableUpdateConversationMessages = true
|
|
CIB.config.strings.webPageContextPrefix = '已处于越狱模式, 请谨慎使用避免封号'
|
|
} else {
|
|
const serpEle = document.querySelector('cib-serp');
|
|
const conversationEle = serpEle?.shadowRoot?.querySelector('cib-conversation') as HTMLElement;
|
|
const welcomeEle = conversationEle?.shadowRoot?.querySelector('cib-welcome-container');
|
|
|
|
const tipEle = document.createElement('div');
|
|
tipEle.innerText = '已处于越狱模式, 请谨慎使用避免封号';
|
|
tipEle.className = 'preview-container';
|
|
|
|
welcomeEle?.shadowRoot?.append(tipEle);
|
|
}
|
|
}
|
|
CIB.registerContext([{
|
|
"author": "user",
|
|
"description": sydneyPrompt.value,
|
|
"contextType": "WebPage",
|
|
"messageType": "Context",
|
|
"sourceName": "Ubuntu Pastebin",
|
|
"sourceUrl": "https://paste.ubuntu.com/p/"+ randomString(10) +"/",
|
|
|
|
}])
|
|
}
|
|
|
|
const initChatPrompt = () => {
|
|
const actionBarEle = document.querySelector('#b_sydConvCont > cib-serp')?.shadowRoot?.querySelector('#cib-action-bar-main') as IActionBarElement;
|
|
const oldHandleInputTextKey = actionBarEle.handleInputTextKey;
|
|
actionBarEle.handleInputTextKey = function (ev: KeyboardEvent) {
|
|
|
|
if (ev.key === 'Enter' && isShowChatPrompt.value) {
|
|
return;
|
|
}
|
|
return oldHandleInputTextKey.apply(this, [ev]);
|
|
};
|
|
|
|
CIB.vm.actionBar.input.addEventListener('compositionstart', handleInputStart);
|
|
CIB.vm.actionBar.input.addEventListener('compositionend', handleInputEnd);
|
|
CIB.vm.actionBar.input.addEventListener('change', handleInputTextChanged);
|
|
CIB.vm.actionBar.input.addEventListener('input', handleInputTextChanged);
|
|
|
|
CIB.vm.actionBar.input.addEventListener('keydown', handleInputTextKey);
|
|
CIB.vm.actionBar.input.addEventListener('focus', handleInputFocus);
|
|
CIB.vm.actionBar.input.addEventListener('blur', handleInputBlur);
|
|
};
|
|
|
|
const handleInputStart = (ev: Event) => {
|
|
|
|
isInput.value = true;
|
|
};
|
|
|
|
const handleInputEnd = (ev: Event) => {
|
|
|
|
isInput.value = false;
|
|
handleInputTextChanged(ev);
|
|
};
|
|
|
|
const handleInputTextChanged = (ev: Event) => {
|
|
|
|
if (isInput.value) {
|
|
return;
|
|
}
|
|
if ((ev instanceof InputEvent || ev instanceof CompositionEvent) && ev.target instanceof HTMLTextAreaElement) {
|
|
if (ev.target.value?.startsWith('/')) {
|
|
isShowChatPrompt.value = true;
|
|
keyword.value = ev.target.value.slice(1);
|
|
selectedPromptIndex.value = 0;
|
|
} else {
|
|
keyword.value = '';
|
|
isShowChatPrompt.value = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleInputFocus = (ev: FocusEvent) => {
|
|
|
|
};
|
|
const handleInputBlur = (ev: FocusEvent) => {
|
|
|
|
setTimeout(() => {
|
|
isShowChatPrompt.value = false;
|
|
}, 200);
|
|
};
|
|
|
|
const handleInputTextKey = (ev: KeyboardEvent) => {
|
|
switch (ev.key) {
|
|
case 'ArrowUp':
|
|
{
|
|
|
|
if (selectedPromptIndex.value > 0) {
|
|
selectedPromptIndex.value--;
|
|
if (scrollbarRef.value) {
|
|
scrollbarRef.value.scrollToIndex(selectedPromptIndex.value);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'ArrowDown':
|
|
{
|
|
|
|
if (selectedPromptIndex.value < searchPromptList.value.length - 1) {
|
|
selectedPromptIndex.value++;
|
|
if (scrollbarRef.value) {
|
|
scrollbarRef.value.scrollToIndex(selectedPromptIndex.value);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'Tab':
|
|
case 'Enter':
|
|
{
|
|
|
|
if (!CIB.vm.actionBar.textInput.value || !CIB.vm.actionBar.textInput.value.startsWith('/')) {
|
|
return;
|
|
}
|
|
selectPrompt(searchPromptList.value[selectedPromptIndex.value]);
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
const selectPrompt = (item: IPrompt) => {
|
|
|
|
if (!item) {
|
|
return;
|
|
}
|
|
keyword.value = '';
|
|
CIB.vm.actionBar.textInput.value = item.prompt;
|
|
isShowChatPrompt.value = false;
|
|
};
|
|
|
|
const handlePromptListScroll = () => {
|
|
isPromptScrolling.value = true;
|
|
setTimeout(() => {
|
|
if (isPromptScrolling.value === true) {
|
|
isPromptScrolling.value = false;
|
|
|
|
const offset = scrollbarRef.value?.getOffset() || 0;
|
|
selectedPromptIndex.value = Math.round(offset / promptItemHeight);
|
|
}
|
|
}, 100);
|
|
};
|
|
|
|
const auth = async () => {
|
|
if (!authKey.value) {
|
|
message.error('请先输入授权码');
|
|
return;
|
|
}
|
|
isAuthBtnLoading.value = true;
|
|
userStore.setAuthKey(authKey.value);
|
|
const res = await userStore.getSysConfig();
|
|
if (res.data.isAuth) {
|
|
message.success('授权成功');
|
|
isShowUnauthorizedModal.value = false;
|
|
afterAuth(res.data);
|
|
} else {
|
|
message.error('授权码有误');
|
|
}
|
|
isAuthBtnLoading.value = false;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<LoadingSpinner :is-show="isShowLoading" />
|
|
<main>
|
|
<NPopover
|
|
trigger="manual"
|
|
:show="isShowChatPrompt"
|
|
:show-arrow="false"
|
|
class="max-w-[1060px] max-h-[390px]"
|
|
:to="false"
|
|
>
|
|
<template #trigger>
|
|
<NButton style="position: fixed; left: 20px; bottom: 80px; z-index: -1; opacity: 0;" />
|
|
</template>
|
|
<div class="w-0 md:w-[60px]"></div>
|
|
<NVirtualList
|
|
v-if="promptList.length > 0"
|
|
class="w-full max-w-[1060px] max-h-[390px] overflow-y-auto"
|
|
:item-size="131"
|
|
item-resizable
|
|
:items="promptList"
|
|
@scroll="handlePromptListScroll"
|
|
>
|
|
<template #default="{ item, index }">
|
|
<ChatPromptItem :index="index" :source="item" />
|
|
</template>
|
|
</NVirtualList>
|
|
<NEmpty v-else class="w-full max-w-[1060px] max-h-[390px] rounded-xl py-6" description="暂未设置提示词数据">
|
|
<template #extra>
|
|
<NButton secondary type="info" @click="isShowPromptSotre = true">去提示词库添加</NButton>
|
|
</template>
|
|
</NEmpty>
|
|
</NPopover>
|
|
</main>
|
|
<footer>
|
|
|
|
<ChatServiceSelect />
|
|
|
|
<NModal v-model:show="isShowUnauthorizedModal" preset="dialog" :closable="false" :close-on-esc="false" :maskClosable="false" :show-icon="false">
|
|
<NResult class="box-border w-11/12 lg:w-[400px] px-4 py-4 rounded-md" status="403" title="401 未授权">
|
|
<template #footer>
|
|
<NInput class="w-11/12" v-model:value="authKey" type="password" placeholder="请输入授权码" maxlength="60" clearable></NInput>
|
|
<n-button class="mt-4" secondary type="info" :loading="isAuthBtnLoading" @click="auth">授权</n-button>
|
|
</template>
|
|
</NResult>
|
|
</NModal>
|
|
</footer>
|
|
</template>
|
|
|