Compare commits
27 Commits
v1.0.0
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d944ee546a | ||
|
|
63cc0f878d | ||
|
|
e9b573a276 | ||
|
|
7acc4949fb | ||
|
|
a672fcad1c | ||
|
|
1a4ee99ff0 | ||
|
|
884f5186a6 | ||
|
|
e96c2da9e5 | ||
|
|
5b8408d7da | ||
|
|
63636fef1f | ||
|
|
3af1c30d49 | ||
|
|
c5d47c680c | ||
|
|
a93a679c71 | ||
|
|
5ff32f2c98 | ||
|
|
196a0c39e7 | ||
|
|
30aba9dfff | ||
|
|
846444c3a2 | ||
|
|
1d8f634a3f | ||
|
|
9c76fcaa3e | ||
|
|
bb661a7737 | ||
|
|
759dc777e1 | ||
|
|
c0197de5c7 | ||
|
|
48faae8988 | ||
|
|
dc815c1595 | ||
|
|
0c6797c6a9 | ||
|
|
c68b479c4d | ||
|
|
f969408c83 |
1
.github/workflows/docker.yaml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
- main
|
||||
paths:
|
||||
- "**"
|
||||
- "!img/**"
|
||||
- "!**.md"
|
||||
- '!docs/**'
|
||||
release:
|
||||
|
||||
25
CHANGELOG.md
Normal 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))
|
||||
61
README.md
@@ -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, LMStudio。OpenAI 提供商可选此项。
|
||||
- `-e PROVIDER_BASE_URL=your-provider-base-url`:设置 LLM 提供商的 API 基础 URL,部分提供商需要设置此项,例如 Ollama、LMStudio、OpenAI 提供商可选此项。例如 `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 展示
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## 致谢
|
||||
|
||||
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`。
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 => {
|
||||
@@ -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);
|
||||
@@ -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[] },
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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 }}
|
||||
@@ -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 }) => {
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
@@ -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__')) {
|
||||
@@ -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';
|
||||
@@ -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 {
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
@@ -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';
|
||||
|
||||
@@ -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() {
|
||||
@@ -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() {
|
||||
@@ -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 (
|
||||
@@ -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';
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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(() => {
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 227 B |
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 601 B |
@@ -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();
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
@@ -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> = [];
|
||||
@@ -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';
|
||||