diff --git a/extension/xuanxuan/extension/xuan/index/ext/ui/index.xuanxuan.html.hook.php b/extension/xuanxuan/extension/xuan/index/ext/ui/index.xuanxuan.html.hook.php
index 38c7df8242..477543d5c4 100644
--- a/extension/xuanxuan/extension/xuan/index/ext/ui/index.xuanxuan.html.hook.php
+++ b/extension/xuanxuan/extension/xuan/index/ext/ui/index.xuanxuan.html.hook.php
@@ -1,11 +1,5 @@
-
+
setClientTheme('blue'); ?>
-
+
+control->loadModel('im');
+$xxdStatus = $im->getXxdStatus();
+if(isset($app->config->xuanxuan->turnon) && $app->config->xuanxuan->turnon && $xxdStatus == 'online')
+{
+ $xuanConfig = new stdclass();
+ $token = $im->userGetAuthToken($app->user->id, 'zentaoweb');
+ $clientUrl = isset($app->config->webClientUrl) ? $app->config->webClientUrl : 'data/xuanxuan/web/index.html';
+ $backendUrl = $im->getServer('zentao');
+
+ $xuanConfig->clientUrl = $clientUrl;
+ $xuanConfig->server = ($app->config->xuanxuan->https == 'on' ? 'https' : 'http') . '://' . parse_url($backendUrl, PHP_URL_HOST) . ':' . $app->config->xuanxuan->commonPort;
+ $xuanConfig->account = $app->user->account;
+ $xuanConfig->authKey = $token->token;
+ $xuanConfig->debug = $app->config->debug;
+}
+?>
+
+
+getWebRoot() . 'data/xuanxuan/sdk/sdk.min.js'); ?>
+
+
diff --git a/lib/zin/wg/chatbtn/v1.php b/lib/zin/wg/chatbtn/v1.php
index 52c0bed9b1..f6dc89bfd0 100644
--- a/lib/zin/wg/chatbtn/v1.php
+++ b/lib/zin/wg/chatbtn/v1.php
@@ -7,39 +7,139 @@ class chatBtn extends wg
public static function getPageCSS(): ?string
{
return << div {margin-bottom: 10px;}
+ #xuan-chat-view .unconfigured {top: 50px;}
+
+ #reload-ai-chat.disabled {cursor: wait; color: #999!important;}
CSS;
}
public static function getPageJS(): ?string
{
- $aiChatURL = createLink('ai', 'chat');
+ global $app;
+
+ $isAIConfigured = $app->control->loadModel('ai')->hasModelsAvailable();
+ $hasAIChatPriv = commonModel::hasPriv('ai', 'chat');
+
+ $xuanUnconfiguredTip = sprintf($app->lang->index->chat->unconfiguredFormat, $app->lang->index->chat->chat, (common::hasPriv('setting', 'xuanxuan') ? sprintf($app->lang->index->chat->goConfigureFormat, helper::createLink('setting', 'xuanxuan'), $app->lang->index->chat->chat) : $app->lang->index->chat->contactAdminForHelp));
+ $aiUnconfiguredTip = sprintf($app->lang->index->chat->unconfiguredFormat, $app->lang->index->chat->ai, (common::hasPriv('ai', 'models') ? sprintf($app->lang->index->chat->goConfigureFormat, helper::createLink('ai', 'models') . '#app=admin', $app->lang->index->chat->ai) : $app->lang->index->chat->contactAdminForHelp));
+ $aiUnauthorizedTip = $app->lang->index->chat->unauthorized;
+ $aiChatURL = createLink('ai', 'chat');
+
+ $aiChatView = "";
+ if(!$hasAIChatPriv) $aiChatView = "$aiUnauthorizedTip
";
+ if(!$isAIConfigured) $aiChatView = "$aiUnconfiguredTip
";
+
$chatContainer = <<
-
HTML;
+
+ $xuanDefaultOpener = ($hasAIChatPriv && $isAIConfigured) ? '' : 'if(window.xuan) $(`#chat-switch .chat-switch-item[data-value="chat"]`).trigger("click");';
+
return << {
- if(!$('#chat-container').length)
- {
- $('body').append(`{$chatContainer}`);
- $('#ai-chat-frame').on('load', () => {window.toggleChatContainer();});
- return;
- }
+ if(window.xuan) window.xuan[window.xuan.shown ? 'expand' : 'show']();
$('#chat-btn').toggleClass('active');
$('#chat-container').toggle();
- }
- $(document).on('click', e =>
- {
- if(!$('#chat-container').length || $('#chat-container').prop('style') && $('#chat-container').prop('style').display !== 'block') return;
- if($(e.target).closest('#ai-chat-view,#chat-btn').length) return;
- window.toggleChatContainer();
- });
+ };
+
+ /* Setup xuan web chat. */
+ window.setupXuan = () => {
+ /* Move xuan web client into #xuan-chat-view. */
+ if(window.xuan && !document.querySelector('#xuan-chat-view #xx-embed-container'))
+ {
+ document.querySelector('#xuan-chat-view').prepend(document.querySelector('#xx-embed-container'));
+
+ /* Switch to xuan chat by default if AI is not available. */
+ $xuanDefaultOpener
+
+ /* Update badge on chat. */
+ window.xuan._options.onNotice = function(notice)
+ {
+ window.handleXuanNoticeChange(notice);
+ $('.chat-switch-item[data-value="chat"]').toggleClass('has-notice', !!notice.count);
+ };
+
+ /* Set style into xuan frame. */
+ let tries = 0;
+ const xuanFrameWaitLoop = setInterval(function()
+ {
+ if(document.querySelector('#xx-embed-container iframe'))
+ {
+ clearInterval(xuanFrameWaitLoop);
+ tries = 0;
+ const chatsViewWaitLoop = setInterval(function()
+ {
+ if(document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu'))
+ {
+ clearInterval(chatsViewWaitLoop);
+ document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu').style.cssText = 'width: 330px !important; top: 48px;';
+ document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-cache').style.cssText = 'right: 330px !important;';
+ document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu-search').style.cssText = 'padding: 10px 8px !important;';
+ document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu-search .btn:first-child').style.cssText = 'display: none;';
+ }
+ else
+ {
+ if(++tries > 20) clearInterval(chatsViewWaitLoop);
+ }
+ }, 200);
+ }
+ else
+ {
+ if(++tries > 20) clearInterval(xuanFrameWaitLoop);
+ }
+ }, 200);
+ }
+ };
+ (() => {
+ /* Insert chat views. */
+ $('body').append(`{$chatContainer}`);
+
+ /* Handle switch events. */
+ $('#chat-switch .chat-switch-item').on('click', e =>
+ {
+ const target = $(e.target);
+ target.addClass('active').siblings().removeClass('active');
+ $('#ai-chat-view').toggle(target.data('value') === 'ai');
+ $('#xuan-chat-view').toggle(target.data('value') === 'chat');
+ });
+
+ /* Handle clicking outside. */
+ $(document).on('click', e =>
+ {
+ if(!$('#chat-container').length || $('#chat-container').prop('style') && $('#chat-container').prop('style').display !== 'block') return;
+ if($(e.target).closest('#chat-switch,#xuan-chat-view>.unconfigured,#ai-chat-view,#chat-btn').length) return;
+ window.toggleChatContainer();
+ });
+ })();
JAVASCRIPT;
}
diff --git a/module/index/ui/index.html.php b/module/index/ui/index.html.php
index 15ecc7c0a9..38f40a77b5 100644
--- a/module/index/ui/index.html.php
+++ b/module/index/ui/index.html.php
@@ -17,8 +17,6 @@ else
$scoreNotice = '';
if($config->vision != 'lite') $scoreNotice = $this->loadModel('score')->getNotice();
-$shouldShowAIChat = hasPriv('ai', 'chat') && $this->loadModel('ai')->hasModelsAvailable();
-
jsVar('scoreNotice', $scoreNotice);
jsVar('edition', $config->edition);
jsVar('vision', $config->vision);
@@ -101,7 +99,7 @@ div
setID('appsToolbar'),
setClass('space-x-1'),
hasPriv('search', 'index') ? globalSearch() : null,
- $shouldShowAIChat ? chatBtn() : null,
+ chatBtn(),
item
(
setClass('ghost btn-zentao px-1'),