diff --git a/app/src/admin/channel.ts b/app/src/admin/channel.ts
index e47eef3..2e04126 100644
--- a/app/src/admin/channel.ts
+++ b/app/src/admin/channel.ts
@@ -205,7 +205,7 @@ export const ChannelInfos: Record = {
"> 密钥即为 *mj-api-secret* (如果没有设置 secret 请填 `null` ) \n" +
"> 白名单即为 *white-list*(如果没有回调 IP 白名单默认接收所有 IP 的回调,不需要加 | 以及后面的内容) \n" +
"> 接入点填写你的 Midjourney Proxy 的部署地址,如 *http://localhost:8080*, *https://example.com* \n" +
- "> 注意:**请在系统设置中设置后端的公网 IP / 域名,否则无法接收回调**",
+ "> 注意:**请在系统设置中设置后端的公网 IP / 域名,否则无法接收回调报错 please provide available notify url** \n",
},
};
diff --git a/app/src/assets/markdown/all.less b/app/src/assets/markdown/all.less
index c01e654..a1a4d03 100644
--- a/app/src/assets/markdown/all.less
+++ b/app/src/assets/markdown/all.less
@@ -84,6 +84,14 @@
top: -34px;
right: 0;
user-select: none;
+ cursor: pointer;
+ transition: 0.25s ease;
+
+ &:hover {
+ p, svg {
+ color: hsl(var(--text-dark));
+ }
+ }
p {
color: hsl(var(--text-secondary-dark));
@@ -91,16 +99,12 @@
line-height: 1;
margin: 0 0 0 6px;
padding: 0;
+ transition: 0.25s ease;
}
svg {
- cursor: pointer;
color: hsl(var(--text-secondary-dark));
- transition: .2s;
-
- &:hover {
- color: hsl(var(--text-dark));
- }
+ transition: 0.25s ease;
}
}
}
diff --git a/app/src/components/Markdown.tsx b/app/src/components/Markdown.tsx
index b6f1a8b..f7bddfa 100644
--- a/app/src/components/Markdown.tsx
+++ b/app/src/components/Markdown.tsx
@@ -15,6 +15,7 @@ import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
import { AppDispatch } from "@/store";
import {
CalendarPlus,
+ Check,
Cloud,
CloudCog,
Cloudy,
@@ -31,7 +32,6 @@ import {
Youtube,
} from "lucide-react";
import { copyClipboard } from "@/utils/dom.ts";
-import { useToast } from "./ui/use-toast.ts";
import { useTranslation } from "react-i18next";
import { parseProgressbar } from "@/components/plugins/progress.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
@@ -49,6 +49,8 @@ import { DialogClose } from "@radix-ui/react-dialog";
import { appLogo } from "@/conf/env.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";
import { useMessageActions } from "@/store/chat.ts";
+import { useTemporaryState } from "@/utils/hook.ts";
+import Icon from "@/components/utils/Icon.tsx";
type MarkdownProps = {
children: string;
@@ -115,9 +117,10 @@ function MarkdownContent({
}: MarkdownProps) {
const dispatch = useDispatch();
const { t } = useTranslation();
- const { toast } = useToast();
const { send: sendAction } = useMessageActions();
+ const { state, triggerState } = useTemporaryState();
+
const subscription = useSelector(subscriptionDataSelector);
useEffect(() => {
@@ -274,22 +277,30 @@ function MarkdownContent({
},
code({ inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || "");
- const language = match ? match[1].toLowerCase() : "";
+ const language = match ? match[1].toLowerCase() : "unknown";
if (language === "file") return parseFile(children.toString());
if (language === "progress")
return parseProgressbar(children.toString());
- return !inline && match ? (
+ if (inline)
+ return (
+
+ {children}
+
+ );
+
+ return (
-
-
{
+ await copyClipboard(children.toString());
+ triggerState();
+ }}
+ >
+ : }
className={`h-3 w-3`}
- onClick={async () => {
- await copyClipboard(children.toString());
- toast({
- title: t("share.copied"),
- });
- }}
/>
{language}
@@ -304,10 +315,6 @@ function MarkdownContent({
className={cn("code-block", codeStyle)}
/>
- ) : (
-
- {children}
-
);
},
}}
diff --git a/app/src/components/Message.tsx b/app/src/components/Message.tsx
index 591f490..bb892f1 100644
--- a/app/src/components/Message.tsx
+++ b/app/src/components/Message.tsx
@@ -66,7 +66,7 @@ function MessageSegment(props: MessageProps) {
return;
props.onFocusLeave && props.onFocusLeave(event);
} catch (e) {
- console.debug(e);
+ console.debug(`[message] cannot leave focus: ${e}`);
}
}}
>
diff --git a/app/src/components/ProjectLink.tsx b/app/src/components/ProjectLink.tsx
index 1c4adc6..22c1624 100644
--- a/app/src/components/ProjectLink.tsx
+++ b/app/src/components/ProjectLink.tsx
@@ -2,6 +2,7 @@ import { Button } from "./ui/button.tsx";
import { useConversationActions, useMessages } from "@/store/chat.ts";
import { MessageSquarePlus } from "lucide-react";
import Github from "@/components/ui/icons/Github.tsx";
+import { openWindow } from "@/utils/device.ts";
function ProjectLink() {
const messages = useMessages();
@@ -21,7 +22,7 @@ function ProjectLink() {
variant="outline"
size="icon"
onClick={() =>
- window.open("https://github.com/Deeptrain-Community/chatnio")
+ openWindow("https://github.com/Deeptrain-Community/chatnio")
}
>
diff --git a/app/src/components/admin/UserTable.tsx b/app/src/components/admin/UserTable.tsx
index 575c4d9..af29a38 100644
--- a/app/src/components/admin/UserTable.tsx
+++ b/app/src/components/admin/UserTable.tsx
@@ -399,7 +399,6 @@ function UserTable() {
{t("admin.is-subscribed")}
{t("admin.level")}
{t("admin.total-month")}
- {useDeeptrain && {t("admin.enterprise")}}
{t("admin.is-banned")}
{t("admin.is-admin")}
{t("admin.action")}
@@ -422,9 +421,6 @@ function UserTable() {
{t(`admin.identity.${userTypeArray[user.level]}`)}
{user.total_month}
- {useDeeptrain && (
- {t(user.enterprise.toString())}
- )}
{t(user.is_banned.toString())}
{t(user.is_admin.toString())}
diff --git a/app/src/components/app/MenuBar.tsx b/app/src/components/app/MenuBar.tsx
index c109976..6fb14fa 100644
--- a/app/src/components/app/MenuBar.tsx
+++ b/app/src/components/app/MenuBar.tsx
@@ -20,6 +20,7 @@ import {
ListStart,
Plug,
Shield,
+ User,
} from "lucide-react";
import { openDialog as openSub } from "@/store/subscription.ts";
import { openDialog as openPackageDialog } from "@/store/package.ts";
@@ -27,9 +28,11 @@ import { openDialog as openInvitationDialog } from "@/store/invitation.ts";
import { openDialog as openSharingDialog } from "@/store/sharing.ts";
import { openDialog as openApiDialog } from "@/store/api.ts";
import router from "@/router.tsx";
-import { useDeeptrain } from "@/conf/env.ts";
+import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
import React from "react";
import { subscriptionDataSelector } from "@/store/globals.ts";
+import { openWindow } from "@/utils/device.ts";
+import { DeeptrainOnly } from "@/conf/deeptrain.tsx";
type MenuBarProps = {
children: React.ReactNode;
@@ -70,12 +73,12 @@ function MenuBar({ children, className }: MenuBarProps) {
{t("sub.title")}
)}
- {useDeeptrain && (
+
dispatch(openPackageDialog())}>
{t("pkg.title")}
- )}
+
dispatch(openInvitationDialog())}>
{t("invitation.invitation")}
@@ -88,6 +91,14 @@ function MenuBar({ children, className }: MenuBarProps) {
{t("api.title")}
+
+ openWindow(`${deeptrainEndpoint}/home`)}
+ >
+
+ {t("my-account")}
+
+
{admin && (
router.navigate("/admin")}>
diff --git a/app/src/components/home/SideBar.tsx b/app/src/components/home/SideBar.tsx
index c28286a..70620ba 100644
--- a/app/src/components/home/SideBar.tsx
+++ b/app/src/components/home/SideBar.tsx
@@ -14,7 +14,7 @@ import { useToast } from "@/components/ui/use-toast.ts";
import { extractMessage, filterMessage } from "@/utils/processor.ts";
import { copyClipboard } from "@/utils/dom.ts";
import { useEffectAsync, useAnimation } from "@/utils/hook.ts";
-import { mobile } from "@/utils/device.ts";
+import { mobile, openWindow } from "@/utils/device.ts";
import { Button } from "@/components/ui/button.tsx";
import { selectMenu, setMenu } from "@/store/menu.ts";
import {
@@ -314,7 +314,7 @@ function SidebarConversationList({
onClick={async (e) => {
e.preventDefault();
e.stopPropagation();
- window.open(shared, "_blank");
+ openWindow(shared, "_blank");
}}
>
{t("share.view")}
diff --git a/app/src/components/home/assemblies/ScrollAction.tsx b/app/src/components/home/assemblies/ScrollAction.tsx
index 1150b77..c950ed2 100644
--- a/app/src/components/home/assemblies/ScrollAction.tsx
+++ b/app/src/components/home/assemblies/ScrollAction.tsx
@@ -25,7 +25,7 @@ function ScrollAction(
const position = target.scrollTop + target.clientHeight;
const height = target.scrollHeight;
const diff = Math.abs(position - height);
- setVisibility(diff > 20);
+ setVisibility(diff > 50);
};
useEffect(() => {
diff --git a/app/src/components/home/subscription/BuyDialog.tsx b/app/src/components/home/subscription/BuyDialog.tsx
index 39b61ea..c12e5c5 100644
--- a/app/src/components/home/subscription/BuyDialog.tsx
+++ b/app/src/components/home/subscription/BuyDialog.tsx
@@ -30,6 +30,8 @@ import { openDialog, quotaSelector } from "@/store/quota.ts";
import { getPlanPrice } from "@/conf/subscription.tsx";
import { Plans } from "@/api/types.tsx";
import { subscriptionDataSelector } from "@/store/globals.ts";
+import { openWindow } from "@/utils/device.ts";
+import { DeeptrainOnly } from "@/conf/deeptrain.tsx";
function countPrice(data: Plans, base: number, month: number): number {
const price = getPlanPrice(data, base) * month;
@@ -100,7 +102,7 @@ async function callBuyAction(
useDeeptrain
? setTimeout(() => {
- window.open(`${deeptrainEndpoint}/home/wallet`);
+ openWindow(`${deeptrainEndpoint}/home/wallet`);
}, 2000)
: dispatch(openDialog());
}
@@ -186,7 +188,7 @@ export function Upgrade({ level, current }: UpgradeProps) {
price: countPrice(subscriptionData, level, month).toFixed(2),
})}
- {useDeeptrain && (
+
(
{t("sub.price-tax", {
@@ -196,7 +198,7 @@ export function Upgrade({ level, current }: UpgradeProps) {
})}
)
- )}
+
diff --git a/app/src/conf/deeptrain.ts b/app/src/conf/deeptrain.ts
deleted file mode 100644
index 7226a93..0000000
--- a/app/src/conf/deeptrain.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { deeptrainAppName, deeptrainEndpoint } from "@/conf/env.ts";
-import { dev } from "@/conf/bootstrap.ts";
-
-export function goDeepLogin() {
- location.href = `${deeptrainEndpoint}/login?app=${
- dev ? "dev" : deeptrainAppName
- }`;
-}
diff --git a/app/src/conf/deeptrain.tsx b/app/src/conf/deeptrain.tsx
new file mode 100644
index 0000000..92959bb
--- /dev/null
+++ b/app/src/conf/deeptrain.tsx
@@ -0,0 +1,17 @@
+import {
+ deeptrainAppName,
+ deeptrainEndpoint,
+ useDeeptrain,
+} from "@/conf/env.ts";
+import { dev } from "@/conf/bootstrap.ts";
+import React from "react";
+
+export function goDeepLogin() {
+ location.href = `${deeptrainEndpoint}/login?app=${
+ dev ? "dev" : deeptrainAppName
+ }`;
+}
+
+export function DeeptrainOnly({ children }: { children: React.ReactNode }) {
+ return useDeeptrain ? <>{children}> : null;
+}
diff --git a/app/src/dialogs/PackageDialog.tsx b/app/src/dialogs/PackageDialog.tsx
index 7d9d43c..988bebb 100644
--- a/app/src/dialogs/PackageDialog.tsx
+++ b/app/src/dialogs/PackageDialog.tsx
@@ -24,6 +24,7 @@ import { Badge } from "@/components/ui/badge.tsx";
import { useEffectAsync } from "@/utils/hook.ts";
import { selectAuthenticated } from "@/store/auth.ts";
import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
+import { openWindow } from "@/utils/device.ts";
function PackageDialog() {
const { t } = useTranslation();
@@ -79,7 +80,7 @@ function PackageDialog() {
diff --git a/app/src/dialogs/QuotaDialog.tsx b/app/src/dialogs/QuotaDialog.tsx
index d75544f..7af25bf 100644
--- a/app/src/dialogs/QuotaDialog.tsx
+++ b/app/src/dialogs/QuotaDialog.tsx
@@ -48,6 +48,7 @@ import {
import { useRedeem } from "@/api/redeem.ts";
import { cn } from "@/components/ui/lib/utils.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";
+import { openWindow } from "@/utils/device.ts";
type AmountComponentProps = {
amount: number;
@@ -240,7 +241,7 @@ function QuotaDialog() {
{
if (!useDeeptrain) {
- window.open(
+ openWindow(
`${buyLink}?quota=${amount}`,
"_blank",
);
@@ -274,7 +275,7 @@ function QuotaDialog() {
});
useDeeptrain &&
setTimeout(() => {
- window.open(
+ openWindow(
`${deeptrainEndpoint}/home/wallet`,
);
}, 2000);
diff --git a/app/src/dialogs/ShareManagementDialog.tsx b/app/src/dialogs/ShareManagementDialog.tsx
index 1d389a6..02c09e3 100644
--- a/app/src/dialogs/ShareManagementDialog.tsx
+++ b/app/src/dialogs/ShareManagementDialog.tsx
@@ -37,6 +37,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu.tsx";
import { getSharedLink, SharingPreviewForm } from "@/api/sharing.ts";
+import { openWindow } from "@/utils/device.ts";
type ShareTableProps = {
data: SharingPreviewForm[];
@@ -79,7 +80,7 @@ function ShareTable({ data }: ShareTableProps) {
{
- window.open(getSharedLink(row.hash), "_blank");
+ openWindow(getSharedLink(row.hash), "_blank");
}}
>
diff --git a/app/src/resources/i18n/cn.json b/app/src/resources/i18n/cn.json
index 83b25da..b20004c 100644
--- a/app/src/resources/i18n/cn.json
+++ b/app/src/resources/i18n/cn.json
@@ -36,7 +36,7 @@
"broadcast": "公告",
"fatal": "应用崩溃",
"download-fatal-log": "下载错误日志",
- "fatal-tips": "请您先检查您的网络,浏览器兼容性,尝试清除浏览器缓存并刷新页面。如果问题仍然存在,请将日志提供给开发者以便我们排查问题。",
+ "fatal-tips": "请您先检查您的网络,浏览器兼容性,尝试清除浏览器缓存并刷新页面。如果问题仍然存在,请下载日志并提供完整复现步骤以给开发者以便我们排查问题。",
"request-error": "请求失败,原因:{{reason}}",
"delete": "删除",
"remove": "移除",
@@ -52,6 +52,7 @@
"min-quota": "最低余额",
"your-quota": "您的余额",
"title": "标题",
+ "my-account": "我的账户",
"auth": {
"username": "用户名",
"username-placeholder": "请输入用户名",
diff --git a/app/src/resources/i18n/en.json b/app/src/resources/i18n/en.json
index 9ecac66..f91347d 100644
--- a/app/src/resources/i18n/en.json
+++ b/app/src/resources/i18n/en.json
@@ -33,7 +33,7 @@
"broadcast": "Broadcast",
"fatal": "App crashed",
"download-fatal-log": "Download error log",
- "fatal-tips": "Please try to check your network, browser compatibility, try to clear the browser cache and refresh the page. If the problem still exists, please provide the log to the developer so that we can troubleshoot the problem.",
+ "fatal-tips": "Please check your web, browser compatibility first, try clearing your browser cache and refreshing the page. If the problem persists, please download the log and provide the complete reproduction steps to the developer so we can troubleshoot the issue.",
"tag": {
"free": "Free",
"official": "Official",
@@ -753,5 +753,6 @@
"model": "Model",
"min-quota": "Minimum Balance",
"your-quota": "Your balance",
- "title": "Title"
+ "title": "Title",
+ "my-account": "My Account"
}
\ No newline at end of file
diff --git a/app/src/resources/i18n/ja.json b/app/src/resources/i18n/ja.json
index 90db4b2..7a330e1 100644
--- a/app/src/resources/i18n/ja.json
+++ b/app/src/resources/i18n/ja.json
@@ -33,7 +33,7 @@
"broadcast": "公告",
"fatal": "アプリのクラッシュ",
"download-fatal-log": "エラーログのダウンロード",
- "fatal-tips": "まずウェブとブラウザの互換性を確認し、ブラウザのキャッシュをクリアしてページを更新してみてください。問題が解決しない場合は、問題のトラブルシューティングを行うために、開発者にログを提供してください。",
+ "fatal-tips": "まずウェブとブラウザの互換性を確認し、ブラウザのキャッシュをクリアしてページを更新してみてください。問題が解決しない場合は、ログをダウンロードして完全な再現手順を開発者に提供し、問題のトラブルシューティングを行います。",
"tag": {
"free": "無料",
"official": "広汽Hondaが",
@@ -753,5 +753,6 @@
"model": "モデル",
"min-quota": "最低残高",
"your-quota": "残高",
- "title": "タイトル"
+ "title": "タイトル",
+ "my-account": "マイアカウント"
}
\ No newline at end of file
diff --git a/app/src/resources/i18n/ru.json b/app/src/resources/i18n/ru.json
index c234776..d024370 100644
--- a/app/src/resources/i18n/ru.json
+++ b/app/src/resources/i18n/ru.json
@@ -33,7 +33,7 @@
"broadcast": "Объявление",
"fatal": "Приложение вылетело",
"download-fatal-log": "Скачать журнал ошибок",
- "fatal-tips": "Пожалуйста, попробуйте проверить свою сеть, совместимость браузера, попробуйте очистить кэш браузера и обновить страницу. Если проблема все еще существует, пожалуйста, предоставьте журнал разработчику, чтобы мы могли устранить проблему.",
+ "fatal-tips": "Сначала проверьте совместимость веб-браузера, попробуйте очистить кэш браузера и обновить страницу. Если проблема не устранена, загрузите журнал и предоставьте разработчику полные инструкции по воспроизведению, чтобы мы могли устранить проблему.",
"tag": {
"free": "Бесплатно",
"official": "Официальный",
@@ -753,5 +753,6 @@
"model": "модель",
"min-quota": "Минимальный баланс",
"your-quota": "Ваш баланс",
- "title": "заглавие"
+ "title": "заглавие",
+ "my-account": "Моя учетная запись"
}
\ No newline at end of file
diff --git a/app/src/utils/app.ts b/app/src/utils/app.ts
index 6acd72a..ce69abb 100644
--- a/app/src/utils/app.ts
+++ b/app/src/utils/app.ts
@@ -1,6 +1,6 @@
import router from "@/router.tsx";
import { useDeeptrain } from "@/conf/env.ts";
-import { goDeepLogin } from "@/conf/deeptrain.ts";
+import { goDeepLogin } from "@/conf/deeptrain.tsx";
export let event: BeforeInstallPromptEvent | undefined;
diff --git a/app/src/utils/device.ts b/app/src/utils/device.ts
index cf82ce4..c150ff1 100644
--- a/app/src/utils/device.ts
+++ b/app/src/utils/device.ts
@@ -40,3 +40,18 @@ export function useMobile(): boolean {
return mobile;
}
+
+export function openWindow(url: string, target?: string): void {
+ /**
+ * Open a new window with the given URL.
+ * If the device does not support opening a new window, the URL will be opened in the current window.
+ * @param url The URL to open.
+ * @param target The target of the URL.
+ */
+
+ if (mobile) {
+ window.location.href = url;
+ } else {
+ window.open(url, target);
+ }
+}
diff --git a/app/src/utils/dom.ts b/app/src/utils/dom.ts
index 7c071a3..2b60be8 100644
--- a/app/src/utils/dom.ts
+++ b/app/src/utils/dom.ts
@@ -321,22 +321,8 @@ export function isContainDom(
*/
if (!el || !target) return false;
- return el.contains(target) && (!notIncludeSelf || el !== target);
-}
-
-export function isContainEventTarget(
- el: HTMLElement | undefined | null,
- e: Event,
-) {
- /**
- * Test if element contains event target
- * @param el Element
- * @param e Event
- * @example
- * const el = document.getElementById("el");
- * const handler = (e: Event) => console.log(isContainEventTarget(el, e));
- * el.addEventListener("click", handler);
- */
-
- return isContainDom(el, e.target as HTMLElement);
+ if (!notIncludeSelf) {
+ return el.contains(target);
+ }
+ return el === target || el.contains(target);
}
diff --git a/app/src/utils/hook.ts b/app/src/utils/hook.ts
index cac47df..f28f9dc 100644
--- a/app/src/utils/hook.ts
+++ b/app/src/utils/hook.ts
@@ -44,36 +44,15 @@ export function useAnimation(
};
}
-export function useShared(): {
- hook: (v: T) => void;
- useHook: () => Promise;
+export function useTemporaryState(interval?: number): {
+ state: boolean;
+ triggerState: () => void;
} {
- /**
- * Share value between components, useful for sharing data between components / redux dispatches
- *
- * @example
- *
- * const dispatch = useDispatch();
- * const { hook, useHook } = useShared();
- *
- * dispatch(updateMigration({ hook }));
- * const response = await useHook();
- */
- let value: T | undefined = undefined;
+ const [stamp, setStamp] = React.useState(0);
+ const triggerState = () => setStamp(new Date().getTime());
+
return {
- hook: (v: T) => {
- value = v;
- },
- useHook: () => {
- return new Promise((resolve) => {
- if (value) return resolve(value);
- const interval = setInterval(() => {
- if (value) {
- clearInterval(interval);
- resolve(value);
- }
- }, 50);
- });
- },
+ state: Date.now() - stamp < (interval ?? 3000),
+ triggerState,
};
}