30 Commits

Author SHA1 Message Date
史悦
177f15a136 避免 Remix revalidation:使用原生 fetch 而非 fetcher.submit,避免触发 Remix 的数据重新验证机制,这是导致流式请求被中止的主要原因。
Some checks failed
CI/CD / Test (push) Has been cancelled
Docker Publish / docker-build-and-push (push) Has been cancelled
增加延迟时间:将项目保存延迟增加到2秒,确保流式请求完全结束后再执行保存操作,减少时间窗口内的冲突。

改进错误处理:添加了更详细的错误处理和日志记录,便于后续调试。
2025-10-15 09:55:47 +08:00
Takagi
c1829e5af9 pref: optimize the diff to make it more accurately reflect changes (#11)
* pref: optimize the diff to make it more accurately reflect changes

* pref: optimize the diff page selection
2025-10-14 12:11:58 +08:00
LIlGG
4e39d41362 fix: ensure nanostores listeners trigger on artifact updates
Refactored artifact update logic to create new Map instances when modifying artifacts, ensuring nanostores listeners are properly triggered. This prevents issues where listeners do not react to in-place mutations of existing Map objects.
2025-10-13 12:42:56 +08:00
Takagi
8c6f55dda8 release: Release v1.0.1 version (#9)
* release: Release v1.0.1 version

* release: Release v1.0.1 version
2025-10-11 19:01:52 +08:00
LIlGG
e9b573a276 refactor: repartition server-side and client-side code 2025-10-11 18:26:07 +08:00
LIlGG
7acc4949fb feat: allow using chat to modify page titles 2025-10-11 16:36:20 +08:00
LIlGG
a672fcad1c fix: switching pages may cause page confusion
Eliminated unnecessary call to webBuilderStore.setActiveSectionByPageName in openArtifactInWebBuilder, as selected page is already set and scrolling to element is handled.
2025-10-11 16:35:02 +08:00
Takagi
1a4ee99ff0 Merge pull request #8 from LIlGG/fix/multi-page-render
fix: resolve logical issues when generating multi-page data
2025-10-11 16:29:24 +08:00
LIlGG
884f5186a6 fix: resolve logical issues when generating multi-page data 2025-10-10 18:48:40 +08:00
Takagi
e96c2da9e5 Merge pull request #7 from LIlGG/fix/loop-call-replace-state
fix: resolve the issue of frequent triggering of replaceState
2025-10-10 14:49:08 +08:00
LIlGG
5b8408d7da fix: resolve the issue of frequent triggering of replaceState 2025-10-10 12:23:42 +08:00
LIlGG
63636fef1f chore: remove tracking script from layout
Deleted the inclusion of the external tracking script in the Layout component, likely to improve privacy or simplify the page head.
2025-10-10 11:32:36 +08:00
LIlGG
3af1c30d49 fix: Reduce the frequency of saving empty pages 2025-10-10 11:29:19 +08:00
LIlGG
c5d47c680c fix: Resolve the issue of possible abnormal text generated during page creation. 2025-10-09 17:48:18 +08:00
LIlGG
a93a679c71 pref: Make the generated page names unique rather than consistent 2025-10-09 14:54:18 +08:00
LIlGG
5ff32f2c98 fix: addressing the issues with outdated prompt information in multi-turn dialogues 2025-10-09 12:02:20 +08:00
LIlGG
196a0c39e7 fix: allow rate limit trust proxy 2025-10-09 11:28:29 +08:00
LIlGG
30aba9dfff docs: correct the quick deployment Docker image name 2025-09-30 18:14:08 +08:00
LIlGG
846444c3a2 docs: update README.md 2025-09-30 18:02:20 +08:00
LIlGG
1d8f634a3f chore: compress document images 2025-09-30 17:40:54 +08:00
LIlGG
9c76fcaa3e Merge branch 'main' of github.com:halo-dev/upage 2025-09-30 17:25:46 +08:00
LIlGG
bb661a7737 docs: supplemental user guide documents 2025-09-30 17:25:35 +08:00
maninhill
759dc777e1 Remove Workflow Status badge from README
Removed GitHub Workflow Status badge from README.
2025-09-30 15:42:04 +08:00
LIlGG
c0197de5c7 docs: add 1panel deployment 2025-09-30 11:12:14 +08:00
maninhill
48faae8988 Update README with 1Panel installation instructions
Added installation instructions for UPage via 1Panel app store.
2025-09-30 10:10:30 +08:00
Takagi
dc815c1595 Update README.md 2025-09-29 22:40:52 +08:00
LIlGG
0c6797c6a9 update README.md 2025-09-29 19:19:28 +08:00
maninhill
c68b479c4d chore: Update README.md 2025-09-29 18:32:03 +08:00
maninhill
f969408c83 chore: Update README.md 2025-09-29 18:20:30 +08:00
Takagi
7ac10b43d1 release: Release v1.0.0 version
release: Release v1.0.0 version
2025-09-29 17:52:24 +08:00
358 changed files with 2478 additions and 1790 deletions

View File

@@ -6,6 +6,7 @@ on:
- main
paths:
- "**"
- "!img/**"
- "!**.md"
- '!docs/**'
release:

25
CHANGELOG.md Normal file
View File

@@ -0,0 +1,25 @@
## <small>[1.0.1](https://github.com/halo-dev/upage/compare/v1.0.0...v1.0.1) (2025-10-11)</small>
### Features
- allow using chat to modify page titles ([7acc494](https://github.com/halo-dev/upage/commit/7acc4949fb4d76a2c5429769ae3d1289ac07fcc5))
### Performance Improvements
- make the generated page names unique rather than consistent ([a93a679](https://github.com/halo-dev/upage/commit/a93a679c712182c5348593df4f1b6a1c8c83ebdd))
- reduce the frequency of saving empty pages ([3af1c30](https://github.com/halo-dev/upage/commit/3af1c30d49c37a69c442e03f2c0bea30a10716ef))
### Bug Fixes
- switching pages may cause page confusion ([a672fca](https://github.com/halo-dev/upage/commit/a672fcad1c6bda7b5ea927826d0218de5e1bc274))
- resolve logical issues when generating multi-page data ([884f518](https://github.com/halo-dev/upage/commit/884f5186a6ded44832555c03661683d19ad23201))
- resolve the issue of frequent triggering of replaceState ([5b8408d](https://github.com/halo-dev/upage/commit/5b8408d7da272f66ee7bce139d56d450158fc86b))
- allow rate limit trust proxy ([196a0c3](https://github.com/halo-dev/upage/commit/196a0c39e7f970c9dc161d046d2530da053f8004))
- addressing the issues with outdated prompt information in multi-turn dialogues ([5ff32f2](https://github.com/halo-dev/upage/commit/5ff32f2c987cf8b9fa3494a5b72f4b46c48abc90))
- resolve the issue of possible abnormal text generated during page creation. ([c5d47c6](https://github.com/halo-dev/upage/commit/c5d47c680ce736f54aa7b35974e2317be0d73146))
### Miscellaneous Chores
- remove tracking script from layout ([63636fe](https://github.com/halo-dev/upage/commit/63636fef1f32130079f7ce38bb21a25e37b80cb3))
### Code Refactoring
- repartition server-side and client-side code ([e9b573a](https://github.com/halo-dev/upage/commit/e9b573a2762e86c4c4df066baa1c33c93436bdc4))

View File

@@ -1,37 +1,26 @@
<p align="center">
<img alt="UPage 主界面" src="./public/logo.png" style="width: 240px; height: auto;" />
<img alt="UPage logo" src="./public/logo.png" style="width: 240px; height: auto;" />
</p>
<h3 align="center">基于大模型的可视化网页构建平台</h3>
<p align="center">
<a href="https://github.com/halo-dev/upage/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/halo-dev/upage.svg?style=flat-square&include_prereleases" /></a>
<a href="https://github.com/halo-dev/upage/commits"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/halo-dev/upage.svg?style=flat-square" /></a>
<a href="https://github.com/halo-dev/upage/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/halo-dev/upage/halo.yaml?branch=main&style=flat-square" /></a>
<a href="https://halo-dev.github.io/upage/"><img alt="Documentation" src="https://img.shields.io/badge/docs-latest-blue?style=flat-square" /></a>
</p>
------------------------------
UPage 是一款基于大模型的可视化网页构建平台,支持多种 AI 提供商集成基于自然语言快速实现定制化网页。UPage 优势在于:
UPage 是一款基于大语言模型的可视化网页构建平台,支持接入主流大模型,只需通过自然语言描述需求,即可快速生成个性化、高颜值的网页,让创作更高效、更智能。
- **基于 LLM 的页面生成**:通过自然语言描述生成完整的网页
- **多种 LLM 提供商支持**:兼容 OpenAI、Anthropic Claude、Google Gemini 等多种 LLM 模型
- **可视化编辑器**:简洁直观的可视化编辑器界面,实时预览
- **多页面生成**:支持同时生成多个页面
- **代码导出**:生成标准的 HTML/CSS/JS 代码,方便集成到现有项目
- **响应式设计**:自动适应不同屏幕尺寸
- **部署集成**:支持一键部署到常见托管平台
------------------------------
特别感谢 [bolt.diy](https://github.com/stackblitz-labs/bolt.diy) 项目UPage 的实现基于该项目的代码结构。
------------------------------
- **可视化编辑,所见即所得**:简洁直观的可视化编辑器,支持实时预览,轻松调整布局与样式;
- **多页面一键生成**:支持同时生成多个关联页面,快速搭建完整网站结构;
- **标准代码自由导出**:自动生成规范的 HTML/CSS/JS 代码,便于集成至现有项目或二次开发;
- **响应式设计,全端适配**:自动适配桌面、平板、移动端等多种设备,确保跨平台完美呈现。
## 快速开始
UPage 提供基于 Docker 的部署方案,可以使用以下脚本进行快速部署
准备一台 Linux 服务器,安装好 Docker 后,执行以下一键安装脚本
```bash
docker run -d \
@@ -49,9 +38,9 @@ docker run -d \
halohub/upage:latest
```
其中参数说明如下:
参数说明如下:
- `-e LLM_PROVIDER=OpenAI`:设置默认的 LLM 提供商为 OpenAI同时兼容支持 OpenAI 规范的 API 接口。
- `-e PROVIDER_BASE_URL=your-provider-base-url`:设置 LLM 提供商的 API 基础 URL部分提供商需要设置此项例如 Ollama, LMStudioOpenAI 提供商可选此项。
- `-e PROVIDER_BASE_URL=your-provider-base-url`:设置 LLM 提供商的 API 基础 URL部分提供商需要设置此项例如 OllamaLMStudioOpenAI 提供商可选此项。例如 `https://api.openai.com/v1`
- `-e PROVIDER_API_KEY=your-openai-api-key`:设置 LLM 提供商的 API 密钥,大部分提供商需要设置此项。
- `-e LLM_DEFAULT_MODEL=your-default-model`:设置默认的 LLM 模型,用于构建页面。
- `-e LLM_MINOR_MODEL=your-minor-model`:设置次要的 LLM 模型,用于执行其他任务。
@@ -61,6 +50,27 @@ docker run -d \
访问 `http://localhost:3000` 即可访问 UPage 的界面。
你也可以通过 [1Panel 应用商店](https://1panel.cn/) 来安装部署 UPage。
详细使用指南请参考:[UPage 在线文档](https://docs.upage.ai/quick-start)
### 联系我们
如你有更多问题,可以加入我们的技术交流群与我们交流。
<img width="180" height="180" alt="contact_me_qr" src="./img/wecom.png">
## UI 展示
| | |
| --- | --- |
| ![](./img/preview-4.png) | ![](./img/preview-1.png) |
| ![](./img/preview-2.png) | ![](./img/preview-3.png) |
## 致谢
UPage 基于 [bolt.diy](https://github.com/stackblitz-labs/bolt.diy) 的代码结构构建,特此致谢该项目带来的启发与贡献。
## 飞致云旗下的其他明星项目
- [Halo](https://github.com/halo-dev/halo) - 强大易用的开源建站工具
@@ -71,12 +81,13 @@ docker run -d \
- [Cordys CRM](https://github.com/cordys/cordys-crm) - 新一代的开源 AI CRM 系统
- [MeterSphere](https://github.com/metersphere/metersphere) - 新一代的开源持续测试工具
## 许可证
## License
本仓库遵循 [FIT2CLOUD Open Source License](https://github.com/halo-dev/upage/blob/main/LICENSE.txt) 开源协议,该许可证本质上是 GPLv3但有一些额外的限制。
本仓库遵循 [FIT2CLOUD Open Source License](LICENSE) 开源协议,该许可证本质上是 GPLv3但有一些额外的限制。
你可以基于 UPage 的源代码进行二次开发,但是需要遵守以下规定:
不能替换和修改 UPage 的 Logo 和版权信息;
二次开发后的衍生作品必须遵守 GPL V3 的开源义务。
如需商业授权,请联系 support@fit2cloud.com 。
- 不能替换和修改 UPage 的 Logo 和版权信息;
- 二次开发后的衍生作品必须遵守 GPL V3 的开源义务。
如需商业授权,请联系:`support@fit2cloud.com`

View File

@@ -1,5 +1,5 @@
import type { LogEntry } from '~/lib/stores/logs';
import { logStore } from '~/lib/stores/logs';
import type { LogEntry } from '~/stores/logs';
import { logStore } from '~/stores/logs';
export interface Notification {
id: string;

View File

@@ -4,11 +4,11 @@ interface BridgeContext {
loaded: boolean;
}
export const bridgeContext: BridgeContext = import.meta.hot?.data.editorBridgeContext ?? {
export const bridgeContext: BridgeContext = import.meta.hot?.data?.editorBridgeContext ?? {
loaded: false,
};
if (import.meta.hot) {
if (import.meta.hot && import.meta.hot.data) {
import.meta.hot.data.editorBridgeContext = bridgeContext;
}
@@ -34,7 +34,7 @@ type SectionProps = {
};
/**
* node editor bridge
* editor bridge
* bridge 广
* pages sections editor
*/
@@ -101,6 +101,26 @@ export class EditorBridge {
});
}
/**
*
*
* @param pageName
* @param param1
* @returns
*/
async updatePageAttributes(pageName: string, { title }: { title?: string } = {}) {
const page = this.#pages.get(pageName);
if (!page) {
return;
}
const actionIds = page.actionIds;
this.#emit('upsert_page', {
pageName,
title,
actionIds,
});
}
async removePage(pageName: string) {
this.#pages.delete(pageName);
@@ -171,7 +191,7 @@ export let editorBridge: Promise<EditorBridge> = new Promise(() => {
if (!import.meta.env.SSR) {
editorBridge =
import.meta.hot?.data.editorBridge ??
import.meta.hot?.data?.editorBridge ??
Promise.resolve()
.then(() => {
return new EditorBridge();
@@ -181,7 +201,7 @@ if (!import.meta.env.SSR) {
return editorBridge;
});
if (import.meta.hot) {
if (import.meta.hot && import.meta.hot.data) {
import.meta.hot.data.editorBridge = editorBridge;
}
}

View File

@@ -2,7 +2,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import classNames from 'classnames';
import { motion } from 'framer-motion';
import { useMemo } from 'react';
import { useAuth } from '~/lib/hooks/useAuth';
import { useAuth } from '~/.client/hooks/useAuth';
import type { TabType } from './types';
interface AvatarDropdownProps {

View File

@@ -3,22 +3,24 @@ import * as RadixDialog from '@radix-ui/react-dialog';
import classNames from 'classnames';
import { AnimatePresence, motion, type Variants } from 'framer-motion';
import { useEffect, useMemo, useState } from 'react';
import { TabTile } from '~/components/@settings/core/TabTile';
import DebugTab from '~/components/@settings/tabs/debug/DebugTab';
import { EventLogsTab } from '~/components/@settings/tabs/event-logs/EventLogsTab';
import NotificationsTab from '~/components/@settings/tabs/notifications/NotificationsTab';
import SettingsTab from '~/components/@settings/tabs/settings/SettingsTab';
import TaskManagerTab from '~/components/@settings/tabs/task-manager/TaskManagerTab';
import BackgroundRays from '~/components/ui/BackgroundRays';
import { useDebugStatus } from '~/lib/hooks/useDebugStatus';
import { useNotifications } from '~/lib/hooks/useNotifications';
import { profileStore } from '~/lib/stores/profile';
import { resetTabConfiguration, tabConfigurationStore } from '~/lib/stores/settings';
import { logger } from '~/utils/logger';
import { TabTile } from '~/.client/components/@settings/core/TabTile';
import DebugTab from '~/.client/components/@settings/tabs/debug/DebugTab';
import { EventLogsTab } from '~/.client/components/@settings/tabs/event-logs/EventLogsTab';
import NotificationsTab from '~/.client/components/@settings/tabs/notifications/NotificationsTab';
import SettingsTab from '~/.client/components/@settings/tabs/settings/SettingsTab';
import TaskManagerTab from '~/.client/components/@settings/tabs/task-manager/TaskManagerTab';
import BackgroundRays from '~/.client/components/ui/BackgroundRays';
import { useDebugStatus } from '~/.client/hooks/useDebugStatus';
import { useNotifications } from '~/.client/hooks/useNotifications';
import { profileStore } from '~/.client/stores/profile';
import { resetTabConfiguration, tabConfigurationStore } from '~/.client/stores/settings';
import { createScopedLogger } from '~/utils/logger';
import { AvatarDropdown } from './AvatarDropdown';
import { DEFAULT_TAB_CONFIG, TAB_DESCRIPTIONS } from './constants';
import type { Profile, TabType, TabVisibilityConfig } from './types';
const logger = createScopedLogger('ControlPanel');
interface ControlPanelProps {
open: boolean;
onClose: () => void;
@@ -83,8 +85,8 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
};
// Process tabs in priority order
tabConfiguration.developerTabs?.forEach((tab) => processTab(tab as BaseTabConfig));
tabConfiguration.userTabs.forEach((tab) => processTab(tab as BaseTabConfig));
tabConfiguration.developerTabs?.forEach((tab: any) => processTab(tab as BaseTabConfig));
tabConfiguration.userTabs.forEach((tab: any) => processTab(tab as BaseTabConfig));
DEFAULT_TAB_CONFIG.forEach((tab) => processTab(tab as BaseTabConfig));
return devTabs.sort((a, b) => a.order - b.order);

View File

@@ -1,8 +1,8 @@
import * as Tooltip from '@radix-ui/react-tooltip';
import classNames from 'classnames';
import { motion } from 'framer-motion';
import { TAB_ICONS, TAB_LABELS } from '~/components/@settings/core/constants';
import type { TabVisibilityConfig } from '~/components/@settings/core/types';
import { TAB_ICONS, TAB_LABELS } from '~/.client/components/@settings/core/constants';
import type { TabVisibilityConfig } from '~/.client/components/@settings/core/types';
interface TabTileProps {
tab: TabVisibilityConfig;

View File

@@ -3,12 +3,12 @@ import classNames from 'classnames';
import { jsPDF } from 'jspdf';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Badge } from '~/components/ui/Badge';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/Collapsible';
import { Dialog, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
import { Progress } from '~/components/ui/Progress';
import { ScrollArea } from '~/components/ui/ScrollArea';
import { type LogEntry, logStore } from '~/lib/stores/logs';
import { Badge } from '~/.client/components/ui/Badge';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/.client/components/ui/Collapsible';
import { Dialog, DialogRoot, DialogTitle } from '~/.client/components/ui/Dialog';
import { Progress } from '~/.client/components/ui/Progress';
import { ScrollArea } from '~/.client/components/ui/ScrollArea';
import { type LogEntry, logStore } from '~/stores/logs';
interface SystemInfo {
os: string;

View File

@@ -5,9 +5,9 @@ import { motion } from 'framer-motion';
import { jsPDF } from 'jspdf';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'sonner';
import { Dialog, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
import { Switch } from '~/components/ui/Switch';
import { type LogEntry, logStore } from '~/lib/stores/logs';
import { Dialog, DialogRoot, DialogTitle } from '~/.client/components/ui/Dialog';
import { Switch } from '~/.client/components/ui/Switch';
import { type LogEntry, logStore } from '~/stores/logs';
interface SelectOption {
value: string;

View File

@@ -4,7 +4,7 @@ import classNames from 'classnames';
import { formatDistanceToNow } from 'date-fns';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { logStore } from '~/lib/stores/logs';
import { logStore } from '~/stores/logs';
interface NotificationDetails {
type?: string;

View File

@@ -2,9 +2,9 @@ import classNames from 'classnames';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
import type { UserProfile } from '~/components/@settings/core/types';
import { Switch } from '~/components/ui/Switch';
import { isMac } from '~/utils/os';
import type { UserProfile } from '~/.client/components/@settings/core/types';
import { Switch } from '~/.client/components/ui/Switch';
import { isMac } from '~/.client/utils/os';
// Helper to get modifier key symbols/text
const getModifierSymbol = (modifier: string): string => {

View File

@@ -15,7 +15,7 @@ import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
import { Line } from 'react-chartjs-2';
import { toast } from 'sonner';
import { tabConfigurationStore } from '~/lib/stores/settings';
import { tabConfigurationStore } from '~/.client/stores/settings';
// Register ChartJS components
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);

View File

@@ -1,5 +1,5 @@
import { DEFAULT_TAB_CONFIG } from '~/components/@settings/core/constants';
import type { TabType, TabVisibilityConfig } from '~/components/@settings/core/types';
import { DEFAULT_TAB_CONFIG } from '~/.client/components/@settings/core/constants';
import type { TabType, TabVisibilityConfig } from '~/.client/components/@settings/core/types';
export const getVisibleTabs = (
tabConfiguration: { userTabs: TabVisibilityConfig[]; developerTabs?: TabVisibilityConfig[] },

View File

@@ -1,7 +1,7 @@
import { useNavigate } from '@remix-run/react';
import { useEffect, useState } from 'react';
import { Button } from '~/components/ui/Button';
import { useAuth } from '~/lib/hooks/useAuth';
import { Button } from '~/.client/components/ui/Button';
import { useAuth } from '~/.client/hooks/useAuth';
export function SignInButton({ className, children = '登录' }: { className?: string; children?: React.ReactNode }) {
const { signIn, isAuthenticated } = useAuth();

View File

@@ -1,4 +1,4 @@
import { useAuth } from '~/lib/hooks/useAuth';
import { useAuth } from '~/.client/hooks/useAuth';
export function UserProfile({ className }: { className?: string }) {
const { isAuthenticated, userInfo, isLoading } = useAuth();

View File

@@ -2,11 +2,11 @@ import { useStore } from '@nanostores/react';
import classNames from 'classnames';
import { AnimatePresence, motion } from 'framer-motion';
import { computed } from 'nanostores';
import { memo, useEffect, useRef, useState } from 'react';
import { memo, useEffect, useMemo, useRef, useState } from 'react';
import { type BundledLanguage, type BundledTheme, createHighlighter, type HighlighterGeneric } from 'shiki';
import type { ActionState } from '~/lib/runtime/action-runner';
import { webBuilderStore } from '~/lib/stores/web-builder';
import { cubicEasingFn } from '~/utils/easings';
import type { ActionState } from '~/.client/runtime/action-runner';
import { webBuilderStore } from '~/.client/stores/web-builder';
import { cubicEasingFn } from '~/.client/utils/easings';
const highlighterOptions = {
langs: ['shell'],
@@ -14,26 +14,34 @@ const highlighterOptions = {
};
const shellHighlighter: HighlighterGeneric<BundledLanguage, BundledTheme> =
import.meta.hot?.data.shellHighlighter ?? (await createHighlighter(highlighterOptions));
import.meta.hot?.data?.shellHighlighter ?? (await createHighlighter(highlighterOptions));
if (import.meta.hot) {
if (import.meta.hot && import.meta.hot.data) {
import.meta.hot.data.shellHighlighter = shellHighlighter;
}
interface ArtifactProps {
messageId: string;
pageName: string;
}
export const Artifact = memo(({ messageId }: ArtifactProps) => {
export const Artifact = memo(({ messageId, pageName }: ArtifactProps) => {
const userToggledActions = useRef(false);
const [showActions, setShowActions] = useState(false);
const [allActionFinished, setAllActionFinished] = useState(false);
const artifacts = useStore(webBuilderStore.chatStore.artifacts);
const artifact = artifacts[messageId];
const artifact = useMemo(() => {
const artifactsByPageName = artifacts.get(messageId);
if (!artifactsByPageName) {
return undefined;
}
return artifactsByPageName.get(pageName);
}, [artifacts, messageId, pageName]);
const actions = useStore(
computed(artifact.runner.actions, (actions) => {
computed(artifact?.runner.actions!, (actions) => {
return Object.values(actions);
}),
);
@@ -44,11 +52,14 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
};
useEffect(() => {
const actionsMap = artifact.runner.actions.get();
const actionsMap = artifact?.runner.actions.get();
if (!actionsMap) {
return;
}
Object.entries(actionsMap).forEach(([actionId, action]) => {
if (action.status === 'running' || action.status === 'pending') {
artifact.runner.actions.setKey(actionId, {
artifact?.runner.actions.setKey(actionId, {
...action,
status: 'aborted',
});
@@ -61,7 +72,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
setShowActions(true);
}
if (actions.length !== 0 && artifact.type === 'bundled') {
if (actions.length !== 0 && artifact?.type === 'bundled') {
const finished = !actions.find((action) => action.status !== 'complete');
if (allActionFinished !== finished) {
@@ -80,7 +91,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
webBuilderStore.showWorkbench.set(!showWorkbench);
}}
>
{artifact.type == 'bundled' && (
{artifact?.type == 'bundled' && (
<>
<div className="p-4">
{allActionFinished ? (
@@ -101,7 +112,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
</button>
<div className="bg-upage-elements-artifacts-borderColor w-[1px]" />
<AnimatePresence>
{actions.length && artifact.type !== 'bundled' && (
{actions.length && artifact?.type !== 'bundled' && (
<motion.button
initial={{ width: 0 }}
animate={{ width: 'auto' }}
@@ -118,7 +129,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
</AnimatePresence>
</div>
<AnimatePresence>
{artifact.type !== 'bundled' && showActions && actions.length > 0 && (
{artifact?.type !== 'bundled' && showActions && actions.length > 0 && (
<motion.div
className="actions"
initial={{ height: 0 }}

View File

@@ -1,7 +1,7 @@
import { memo } from 'react';
import Popover from '~/components/ui/Popover';
import Tooltip from '~/components/ui/Tooltip';
import type { ParsedUIMessage } from '~/lib/stores/ai-state';
import Popover from '~/.client/components/ui/Popover';
import Tooltip from '~/.client/components/ui/Tooltip';
import type { ParsedUIMessage } from '~/.client/stores/ai-state';
import { Markdown } from './Markdown';
export const AssistantMessage = memo(({ message }: { message: ParsedUIMessage }) => {

View File

@@ -5,10 +5,10 @@ import classNames from 'classnames';
import { useAnimate } from 'framer-motion';
import { useEffect, useState } from 'react';
import { ClientOnly } from 'remix-utils/client-only';
import { useShortcuts, useSnapScroll } from '~/lib/hooks';
import { useChatMessage } from '~/lib/hooks/useChatMessage';
import { aiState, setChatId, setChatStarted } from '~/lib/stores/ai-state';
import { webBuilderStore } from '~/lib/stores/web-builder';
import { useShortcuts, useSnapScroll } from '~/.client/hooks';
import { useChatMessage } from '~/.client/hooks/useChatMessage';
import { aiState, setChatId, setChatStarted } from '~/.client/stores/ai-state';
import { webBuilderStore } from '~/.client/stores/web-builder';
import type { ChatMessage, ChatWithMessages } from '~/types/chat';
import { renderLogger } from '~/utils/logger';
import { Menu } from '../sidebar/Menu.client';

View File

@@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
import classNames from 'classnames';
import { AnimatePresence, motion } from 'framer-motion';
import { useCallback, useMemo } from 'react';
import { webBuilderStore } from '~/lib/stores/web-builder';
import { webBuilderStore } from '~/.client/stores/web-builder';
interface Props {
postMessage: (message: string) => void;

View File

@@ -2,8 +2,8 @@ import { useStore } from '@nanostores/react';
import classNames from 'classnames';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ClientOnly } from 'remix-utils/client-only';
import { useAuth, usePromptEnhancer } from '~/lib/hooks';
import { aiState } from '~/lib/stores/ai-state';
import { useAuth, usePromptEnhancer } from '~/.client/hooks';
import { aiState } from '~/.client/stores/ai-state';
import { IconButton } from '../ui/IconButton';
import { SendButton } from './SendButton.client';

View File

@@ -1,7 +1,7 @@
import { AnimatePresence, motion } from 'framer-motion';
import React, { useState } from 'react';
import { cubicEasingFn } from '~/.client/utils/easings';
import type { ElementInfoMetadata } from '~/types/message';
import { cubicEasingFn } from '~/utils/easings';
import { ElementPreview } from './ElementPreview';
interface ElementEditPreviewProps {

View File

@@ -25,12 +25,16 @@ export const Markdown = memo(({ children, html = false, limitedMarkdown = false
div: ({ className, children, node, ...props }) => {
if (className?.includes('__uPageArtifact__')) {
const messageId = node?.properties.dataMessageId as string;
const pageName = node?.properties.dataPageName as string;
if (!messageId) {
logger.error(`Invalid message id ${messageId}`);
}
if (!pageName) {
logger.error(`Invalid page name ${pageName}`);
}
return <Artifact messageId={messageId} />;
return <Artifact messageId={messageId} pageName={pageName} />;
}
if (className?.includes('__uPageThought__')) {

View File

@@ -4,11 +4,11 @@ import classNames from 'classnames';
import type { ForwardedRef } from 'react';
import { Fragment, forwardRef, memo, useEffect, useMemo, useRef } from 'react';
import { toast } from 'sonner';
import WithTooltip from '~/components/ui/Tooltip';
import { useAuth } from '~/lib/hooks/useAuth';
import { useChatOperate } from '~/lib/hooks/useChatOperate';
import { useSnapScroll } from '~/lib/hooks/useSnapScroll';
import { aiState, type ParsedUIMessage } from '~/lib/stores/ai-state';
import WithTooltip from '~/.client/components/ui/Tooltip';
import { useAuth } from '~/.client/hooks/useAuth';
import { useChatOperate } from '~/.client/hooks/useChatOperate';
import { useSnapScroll } from '~/.client/hooks/useSnapScroll';
import { aiState, type ParsedUIMessage } from '~/.client/stores/ai-state';
import { AssistantMessage } from './AssistantMessage';
import styles from './Messages.module.scss';
import { UserMessage } from './UserMessage';

View File

@@ -1,7 +1,7 @@
import classNames from 'classnames';
import type { KeyboardEvent } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { ModelInfo } from '~/lib/modules/llm/types';
import type { ModelInfo } from '~/.server/modules/llm/types';
import type { ProviderInfo } from '~/types/model';
interface ModelSelectorProps {

View File

@@ -1,6 +1,6 @@
import * as Tooltip from '@radix-ui/react-tooltip';
import { useEffect, useState } from 'react';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { DeploymentPlatformEnum } from '~/types/deployment';
export function NetlifyDeploymentLink() {

View File

@@ -1,8 +1,8 @@
import classNames from 'classnames';
import { AnimatePresence, motion } from 'framer-motion';
import { useMemo, useState } from 'react';
import { cubicEasingFn } from '~/.client/utils/easings';
import type { ProgressAnnotation } from '~/types/message';
import { cubicEasingFn } from '~/utils/easings';
export default function ProgressCompilation({ data }: { data?: ProgressAnnotation[] }) {
const [expanded, setExpanded] = useState(false);

View File

@@ -1,5 +1,5 @@
import classNames from 'classnames';
import { IconButton } from '~/components/ui/IconButton';
import { IconButton } from '~/.client/components/ui/IconButton';
export const SpeechRecognitionButton = ({
isListening,

View File

@@ -1,6 +1,6 @@
import type { FileUIPart } from 'ai';
import { MODEL_REGEX, PROVIDER_REGEX } from '~/.client/utils/constants';
import type { UPageUIMessage } from '~/types/message';
import { MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
import { ElementEditPreview } from './ElementEditPreview';
import { Markdown } from './Markdown';

View File

@@ -1,6 +1,6 @@
import * as Tooltip from '@radix-ui/react-tooltip';
import { useEffect, useState } from 'react';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { DeploymentPlatformEnum } from '~/types/deployment';
export function VercelDeploymentLink() {

View File

@@ -1,6 +1,6 @@
import * as Tooltip from '@radix-ui/react-tooltip';
import { useEffect, useState } from 'react';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { DeploymentPlatformEnum } from '~/types/deployment';
export function _1PanelDeploymentLink() {

View File

@@ -1,5 +1,5 @@
import { IconButton } from '~/components/ui/IconButton';
import WithTooltip from '~/components/ui/Tooltip';
import { IconButton } from '~/.client/components/ui/IconButton';
import WithTooltip from '~/.client/components/ui/Tooltip';
export const ExportChatButton = ({ exportChat }: { exportChat?: () => void }) => {
return (

View File

@@ -2,7 +2,7 @@ import * as RadixDialog from '@radix-ui/react-dialog';
import classNames from 'classnames';
import { motion, type Transition, type Variants } from 'framer-motion';
import { memo } from 'react';
import { useChatUsage } from '~/lib/hooks/useChatUsage';
import { useChatUsage } from '~/.client/hooks/useChatUsage';
import { DialogDescription, DialogTitle } from '../../ui/Dialog';
import { IconButton } from '../../ui/IconButton';
import { ChatUsageVisualization } from './ChatUsageVisualization';

View File

@@ -14,8 +14,8 @@ import {
import classNames from 'classnames';
import { useMemo } from 'react';
import { Doughnut, Line, Pie } from 'react-chartjs-2';
import type { ChatUsageStats } from '~/lib/hooks/useChatUsage';
import { themeStore } from '~/lib/stores/theme';
import type { ChatUsageStats } from '~/.client/hooks/useChatUsage';
import { themeStore } from '~/stores/theme';
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ArcElement, PointElement, LineElement);

View File

@@ -4,7 +4,7 @@ import classNames from 'classnames';
import { motion, type Transition, type Variants } from 'framer-motion';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import { type DeploymentRecord, useDeploymentRecords } from '~/lib/hooks/useDeploymentRecords';
import { type DeploymentRecord, useDeploymentRecords } from '~/.client/hooks/useDeploymentRecords';
import { DeploymentPlatformEnum, DeploymentStatusEnum } from '~/types/deployment';
import { ConfirmationDialog, DialogDescription, DialogTitle } from '../../ui/Dialog';
import { IconButton } from '../../ui/IconButton';

View File

@@ -1,5 +1,5 @@
import React, { useRef } from 'react';
import { sendChatMessageStore } from '~/lib/stores/chat-message';
import { sendChatMessageStore } from '~/.client/stores/chat-message';
import { DefaultEditor } from './editors/DefaultEditor';
import type { EditorProps } from './editors/EditorProps';
import { IconEditor } from './editors/IconEditor';

View File

@@ -1,10 +1,10 @@
import { memo, useCallback, useEffect, useRef } from 'react';
import { useChatHistory } from '~/lib/persistence';
import { useChatHistory } from '~/.client/persistence';
import { isValidContent } from '~/.client/utils/html-parse';
import { throttleWithTrailing } from '~/.client/utils/throttle';
import type { Section } from '~/types/actions';
import type { DocumentProperties, Editor } from '~/types/editor';
import { isValidContent } from '~/utils/html-parse';
import { logger } from '~/utils/logger';
import { throttleWithTrailing } from '~/utils/throttle';
import { EditorComponent } from './EditorComponent';
export interface ScrollPosition {
@@ -28,7 +28,6 @@ interface Props {
editable?: boolean;
debounceChange?: number;
debounceScroll?: number;
autoFocusOnDocumentChange?: boolean;
onChange?: OnChangeCallback;
onReset?: () => void;
onSave?: OnSaveCallback;
@@ -39,7 +38,7 @@ interface Props {
}
export const EditorStudio = memo(
({ documents, currentPage, currentSection, autoFocusOnDocumentChange, onChange, onSave, onLoad, onReady }: Props) => {
({ documents, currentPage, currentSection, onChange, onSave, onLoad, onReady }: Props) => {
const editorRef = useRef<Editor | null>(null);
const pendingSectionRef = useRef<Section | null>(null);
@@ -134,7 +133,7 @@ export const EditorStudio = memo(
// 保存最新的页面属性,确保在节流期间如果有新的更新进来,会使用最新的数据
pendingSectionRef.current = currentSection;
setEditorDocument(editor, currentSection);
}, [currentSection, autoFocusOnDocumentChange]);
}, [currentSection]);
// 确保在组件卸载前应用最后一次更新
useEffect(() => {
@@ -165,7 +164,7 @@ export const EditorStudio = memo(
[onSave],
);
const handleAutoSave = useCallback(async () => {
const handleSave = useCallback(async () => {
const editor = editorRef.current;
if (!editor) {
return;
@@ -193,7 +192,7 @@ export const EditorStudio = memo(
documents={documents}
onLoad={handleLoad}
onReady={handleEditorReady}
onSave={handleAutoSave}
onSave={handleSave}
onContentChange={handleContentChange}
/>
);

View File

@@ -1,6 +1,6 @@
import type { RefObject } from 'react';
import { createRef, useCallback, useEffect, useRef } from 'react';
import { useEditorCommands } from '~/lib/hooks';
import { useEditorCommands } from '~/.client/hooks';
import type { DocumentProperties, Editor } from '~/types/editor';
import { EditorController } from './EditorController';
import { EditorRender } from './EditorRender';

View File

@@ -1,6 +1,6 @@
import { executeScript } from '~/.client/utils/execute-scripts';
import { isScriptContent } from '~/.client/utils/html-parse';
import type { Editor, EditorControllerProps } from '~/types/editor';
import { executeScript } from '~/utils/execute-scripts';
import { isScriptContent } from '~/utils/html-parse';
export class EditorController implements Editor {
private props: EditorControllerProps;

View File

@@ -1,9 +1,9 @@
import { motion, type Variants } from 'framer-motion';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import Frame from 'react-frame-component';
import { executeScripts } from '~/.client/utils/execute-scripts';
import { isMac } from '~/.client/utils/os';
import type { DocumentProperties } from '~/types/editor';
import { executeScripts } from '~/utils/execute-scripts';
import { isMac } from '~/utils/os';
import { EditorOverlay } from './EditorOverlay';
export interface PageRenderRef {

View File

Before

Width:  |  Height:  |  Size: 227 B

After

Width:  |  Height:  |  Size: 227 B

View File

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 601 B

View File

@@ -1,10 +1,10 @@
import { useStore } from '@nanostores/react';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import { useEffect } from 'react';
import WithTooltip from '~/components/ui/Tooltip';
import { useEditChatDescription } from '~/lib/hooks';
import { useChatHistory } from '~/lib/hooks/useChatHistory';
import { webBuilderStore } from '~/lib/stores/web-builder';
import WithTooltip from '~/.client/components/ui/Tooltip';
import { useEditChatDescription } from '~/.client/hooks';
import { useChatHistory } from '~/.client/hooks/useChatHistory';
import { webBuilderStore } from '~/.client/stores/web-builder';
export function ChatDescription() {
const { getChatLatestDescription } = useChatHistory();

View File

@@ -2,9 +2,9 @@ import { useStore } from '@nanostores/react';
import * as Dialog from '@radix-ui/react-dialog';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import _1PanelConnection from '~/components/header/connections/_1PanelConnection';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { _1PanelConnectionStore } from '~/lib/stores/1panel';
import _1PanelConnection from '~/.client/components/header/connections/_1PanelConnection';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { _1PanelConnectionStore } from '~/.client/stores/1panel';
import { DeploymentPlatformEnum } from '~/types/deployment';
interface DeployTo1PanelDialogProps {

View File

@@ -2,11 +2,11 @@ import { useStore } from '@nanostores/react';
import * as Dialog from '@radix-ui/react-dialog';
import { motion } from 'framer-motion';
import React, { Suspense, useEffect, useState } from 'react';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { netlifyConnection } from '~/lib/stores/netlify';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { netlifyConnection } from '~/.client/stores/netlify';
import { DeploymentPlatformEnum } from '~/types/deployment';
const NetlifyConnection = React.lazy(() => import('~/components/header/connections/NetlifyConnection'));
const NetlifyConnection = React.lazy(() => import('~/.client/components/header/connections/NetlifyConnection'));
interface DeployToNetlifyDialogProps {
isOpen: boolean;

View File

@@ -2,9 +2,9 @@ import { useStore } from '@nanostores/react';
import * as Dialog from '@radix-ui/react-dialog';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import VercelConnection from '~/components/header/connections/VercelConnection';
import { useChatDeployment } from '~/lib/hooks/useChatDeployment';
import { vercelConnection } from '~/lib/stores/vercel';
import VercelConnection from '~/.client/components/header/connections/VercelConnection';
import { useChatDeployment } from '~/.client/hooks/useChatDeployment';
import { vercelConnection } from '~/.client/stores/vercel';
import { DeploymentPlatformEnum } from '~/types/deployment';
interface DeployToVercelDialogProps {

View File

@@ -2,9 +2,9 @@ import { useStore } from '@nanostores/react';
import classNames from 'classnames';
import { useMemo } from 'react';
import { ClientOnly } from 'remix-utils/client-only';
import { useAuth } from '~/lib/hooks';
import { aiState } from '~/lib/stores/ai-state';
import { themeStore } from '~/lib/stores/theme';
import { useAuth } from '~/.client/hooks';
import { aiState } from '~/.client/stores/ai-state';
import { themeStore } from '~/stores/theme';
import { HistorySwitch } from '../sidebar/HistorySwitch';
import { ThemeSwitch } from '../ui/ThemeSwitch';
import { ChatDescription } from './ChatDescription.client';

View File

@@ -4,11 +4,11 @@ import classNames from 'classnames';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { toast } from 'sonner';
import { NetlifyDeploymentLink } from '~/components/chat/NetlifyDeploymentLink.client';
import useViewport from '~/lib/hooks';
import { setLocalStorage } from '~/lib/persistence';
import { aiState, setShowChat } from '~/lib/stores/ai-state';
import { webBuilderStore } from '~/lib/stores/web-builder';
import { NetlifyDeploymentLink } from '~/.client/components/chat/NetlifyDeploymentLink.client';
import useViewport from '~/.client/hooks';
import { setLocalStorage } from '~/.client/persistence';
import { aiState, setShowChat } from '~/.client/stores/ai-state';
import { webBuilderStore } from '~/.client/stores/web-builder';
import type { _1PanelDeployResponse } from '~/types/1panel';
import { DeploymentPlatformEnum } from '~/types/deployment';
import type { ApiResponse } from '~/types/global';

View File

@@ -2,12 +2,12 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import classNames from 'classnames';
import { motion } from 'framer-motion';
import { useMemo, useState } from 'react';
import { ChatUsageDialog } from '~/components/chat/usage/ChatUsageDialog';
import { DeploymentRecordsDialog } from '~/components/chat/usage/DeploymentRecordsDialog';
import { Button } from '~/components/ui/Button';
import { ConfirmationDialog } from '~/components/ui/Dialog';
import { useAuth } from '~/lib/hooks/useAuth';
import { useChatUsage } from '~/lib/hooks/useChatUsage';
import { ChatUsageDialog } from '~/.client/components/chat/usage/ChatUsageDialog';
import { DeploymentRecordsDialog } from '~/.client/components/chat/usage/DeploymentRecordsDialog';
import { Button } from '~/.client/components/ui/Button';
import { ConfirmationDialog } from '~/.client/components/ui/Dialog';
import { useAuth } from '~/.client/hooks/useAuth';
import { useChatUsage } from '~/.client/hooks/useChatUsage';
interface MinimalAvatarDropdownProps {}

View File

@@ -2,9 +2,9 @@ import classNames from 'classnames';
import Cookies from 'js-cookie';
import React, { useEffect, useState } from 'react';
import { toast } from 'sonner';
import { Button } from '~/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/Collapsible';
import { logStore } from '~/lib/stores/logs';
import { Button } from '~/.client/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/.client/components/ui/Collapsible';
import { logStore } from '~/stores/logs';
import ConnectionBorder from './components/ConnectionBorder';
interface GitHubUserResponse {

View File

@@ -5,10 +5,15 @@ import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale/zh-CN';
import { useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Badge } from '~/components/ui/Badge';
import { Button } from '~/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/Collapsible';
import { fetchNetlifyStats, isFetchingStats, netlifyConnection, updateNetlifyConnection } from '~/lib/stores/netlify';
import { Badge } from '~/.client/components/ui/Badge';
import { Button } from '~/.client/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/.client/components/ui/Collapsible';
import {
fetchNetlifyStats,
isFetchingStats,
netlifyConnection,
updateNetlifyConnection,
} from '~/.client/stores/netlify';
import type { ConnectionSettings } from '~/root';
import type { ApiResponse } from '~/types/global';
import type { NetlifyBuild, NetlifyDeploy, NetlifySite } from '~/types/netlify';

View File

@@ -3,9 +3,9 @@ import { useFetcher, useRouteLoaderData } from '@remix-run/react';
import classNames from 'classnames';
import React, { useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import { logStore } from '~/lib/stores/logs';
import { fetchVercelStats, isFetchingStats, updateVercelConnection, vercelConnection } from '~/lib/stores/vercel';
import { fetchVercelStats, isFetchingStats, updateVercelConnection, vercelConnection } from '~/.client/stores/vercel';
import type { ConnectionSettings } from '~/root';
import { logStore } from '~/stores/logs';
import { logger } from '~/utils/logger';
import ConnectionBorder from './components/ConnectionBorder';

View File

@@ -6,11 +6,16 @@ import { zhCN } from 'date-fns/locale/zh-CN';
import { motion } from 'framer-motion';
import React, { useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Badge } from '~/components/ui/Badge';
import { Button } from '~/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/Collapsible';
import { _1PanelConnectionStore, fetch1PanelStats, isFetchingStats, update1PanelConnection } from '~/lib/stores/1panel';
import { getChatId } from '~/lib/stores/ai-state';
import { Badge } from '~/.client/components/ui/Badge';
import { Button } from '~/.client/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/.client/components/ui/Collapsible';
import {
_1PanelConnectionStore,
fetch1PanelStats,
isFetchingStats,
update1PanelConnection,
} from '~/.client/stores/1panel';
import { getChatId } from '~/.client/stores/ai-state';
import type { ConnectionSettings } from '~/root';
import type { _1PanelWebsite } from '~/types/1panel';
import type { ApiResponse } from '~/types/global';

View File

@@ -4,14 +4,14 @@ import classNames from 'classnames';
import { motion } from 'framer-motion';
import React, { Suspense, useEffect, useState } from 'react';
import { toast } from 'sonner';
import { getLocalStorage } from '~/lib/persistence';
import { logStore } from '~/lib/stores/logs';
import { webBuilderStore } from '~/lib/stores/web-builder';
import { getLocalStorage } from '~/.client/persistence';
import { webBuilderStore } from '~/.client/stores/web-builder';
import { formatSize } from '~/.client/utils/format';
import { logStore } from '~/stores/logs';
import type { GitHubUserResponse } from '~/types/github';
import { formatSize } from '~/utils/format';
import { logger } from '~/utils/logger';
const GitHubConnection = React.lazy(() => import('~/components/header/connections/GithubConnection'));
const GitHubConnection = React.lazy(() => import('~/.client/components/header/connections/GithubConnection'));
interface PushToGitHubDialogProps {
isOpen: boolean;

View File

@@ -1,10 +1,10 @@
import { useParams } from '@remix-run/react';
import classNames from 'classnames';
import { type ForwardedRef, forwardRef, useCallback } from 'react';
import { Checkbox } from '~/components/ui/Checkbox';
import WithTooltip from '~/components/ui/Tooltip';
import { useEditChatDescription } from '~/lib/hooks';
import type { ServerChatItem } from '~/lib/hooks/useChatEntries';
import { Checkbox } from '~/.client/components/ui/Checkbox';
import WithTooltip from '~/.client/components/ui/Tooltip';
import { useEditChatDescription } from '~/.client/hooks';
import type { ServerChatItem } from '~/.client/hooks/useChatEntries';
interface HistoryItemProps {
item: ServerChatItem;

View File

@@ -1,6 +1,6 @@
import { memo, useEffect, useState } from 'react';
import { IconButton } from '~/components/ui/IconButton';
import { toggleSidebar } from '~/lib/stores/sidebar';
import { IconButton } from '~/.client/components/ui/IconButton';
import { toggleSidebar } from '~/.client/stores/sidebar';
interface HistorySwitchProps {
className?: string;

View File

@@ -3,15 +3,15 @@ import classNames from 'classnames';
import { motion, type Variants } from 'framer-motion';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'sonner';
import { ControlPanel } from '~/components/@settings/core/ControlPanel';
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
import { SettingsButton } from '~/components/ui/SettingsButton';
import { useAuth } from '~/lib/hooks';
import { type ServerChatItem, useChatEntries } from '~/lib/hooks/useChatEntries';
import { useChatOperate } from '~/lib/hooks/useChatOperate';
import { aiState } from '~/lib/stores/ai-state';
import { sidebarStore } from '~/lib/stores/sidebar';
import { cubicEasingFn } from '~/utils/easings';
import { ControlPanel } from '~/.client/components/@settings/core/ControlPanel';
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/.client/components/ui/Dialog';
import { SettingsButton } from '~/.client/components/ui/SettingsButton';
import { useAuth } from '~/.client/hooks';
import { type ServerChatItem, useChatEntries } from '~/.client/hooks/useChatEntries';
import { useChatOperate } from '~/.client/hooks/useChatOperate';
import { aiState } from '~/.client/stores/ai-state';
import { sidebarStore } from '~/.client/stores/sidebar';
import { cubicEasingFn } from '~/.client/utils/easings';
import WithTooltip from '../ui/Tooltip';
import { binDates } from './date-binning';
import { HistoryItem } from './HistoryItem.client';

View File

@@ -1,11 +1,11 @@
import { format, isAfter, isThisWeek, isThisYear, isToday, isYesterday, subDays } from 'date-fns';
import { zhCN } from 'date-fns/locale/zh-CN';
import type { ServerChatItem } from '~/lib/hooks/useChatEntries';
import type { ServerChatItem } from '~/.client/hooks/useChatEntries';
type Bin = { category: string; items: ServerChatItem[] };
export function binDates(_list: ServerChatItem[]) {
const list = _list.toSorted((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp));
const list = _list.slice().sort((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp));
const binLookup: Record<string, Bin> = {};
const bins: Array<Bin> = [];

View File

@@ -3,7 +3,7 @@ import classNames from 'classnames';
import { motion, type Variants } from 'framer-motion';
import React, { memo, type ReactNode, useEffect, useMemo, useState } from 'react';
import { List, type RowComponentProps } from 'react-window';
import { cubicEasingFn } from '~/utils/easings';
import { cubicEasingFn } from '~/.client/utils/easings';
import { Button } from './Button';
import { Checkbox } from './Checkbox';
import { IconButton } from './IconButton';

Some files were not shown because too many files have changed in this diff Show More