Skip to content

Commit

Permalink
Merge pull request #444 from refly-ai/feat/deploy-extension
Browse files Browse the repository at this point in the history
Feat/deploy extension
  • Loading branch information
pftom authored Feb 6, 2025
2 parents e76c315 + c2744e4 commit 5efc901
Show file tree
Hide file tree
Showing 28 changed files with 604 additions and 61 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

Refly is an open-source AI-native creation engine. It's intuitive free-form canvas interface combines multi-threaded conversations, knowledge base RAG integration, contextual memory, intelligent search, WYSIWYG AI editor and more, empowering you to effortlessly transform ideas into production-ready content.

[🚀 Refly v0.2.3 Released! Featuring Enhanced Product Onboarding!](https://docs.refly.ai/changelog/v0.2.3)
[🚀 Refly Chrome Extension web clipper launched! ⚡️!](https://docs.refly.ai/guide/chrome-extension)

[Refly Cloud](https://refly.ai/) · [Self-hosting](https://refly.ai/) · [Forum](https://github.com/refly-ai/refly/discussions) · [Discord](https://discord.gg/bWjffrb89h) · [Twitter](https://x.com/reflyai) · [Documentation](https://docs.refly.ai/)

Expand Down
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

Refly 是一个开源的 AI 原生创作引擎。Refly 直观的自由画布界面集成了多线程对话、RAG 检索流程、上下文记忆、智能搜索和 AI 文档编辑等功能,让您轻松地将创意转化为完整作品。

[🚀 Refly v0.2.3 正式发布!全新升级产品引导体验!](https://docs.refly.ai/changelog/v0.2.3)
[🚀 Refly 网页剪存 Chrome 插件发布! ⚡️! ](https://docs.refly.ai/guide/chrome-extension)

[Refly Cloud](https://refly.ai/) · [Self-hosting](https://refly.ai/) · [Forum](https://github.com/refly-ai/refly/discussions) · [Discord](https://discord.gg/bWjffrb89h) · [Twitter](https://x.com/reflyai) · [Documentation](https://docs.refly.ai/)

Expand Down
2 changes: 1 addition & 1 deletion apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@refly/extension",
"description": "A free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and AI documents, easily transforms ideas into quality content.",
"private": true,
"version": "0.1.0",
"version": "0.3.6",
"type": "module",
"scripts": {
"predev": "wxt prepare",
Expand Down
5 changes: 5 additions & 0 deletions apps/extension/src/components/content-clipper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const ContentClipper: React.FC<ContentClipperProps> = ({ className, onSav
content: '',
});
const [isSaving, setIsSaving] = useState(false);
const [isClipping, setIsClipping] = useState(false);
const { saveSelectedContent } = useSaveSelectedContent();
const { handleSaveResourceAndNotify } = useSaveResourceNotify();

Expand All @@ -45,20 +46,23 @@ export const ContentClipper: React.FC<ContentClipperProps> = ({ className, onSav
if (response?.body) {
setPageInfo(response.body);
}
setIsClipping(false);
}
}, getRuntime());
}, []);

// Handle clip current page content
const handleClipContent = useCallback(async () => {
try {
setIsClipping(true);
// Send message to content script to get page content
const msg: BackgroundMessage = {
source: getRuntime(),
name: 'getPageContent',
};
sendMessage(msg);
} catch (err) {
setIsClipping(false);
console.error('Failed to clip content:', err);
message.error(t('extension.webClipper.error.clipContentFailed'));
}
Expand Down Expand Up @@ -176,6 +180,7 @@ export const ContentClipper: React.FC<ContentClipperProps> = ({ className, onSav
size="large"
icon={<HiOutlineDocumentDownload />}
onClick={handleClipContent}
loading={isClipping}
className="flex-1"
>
{t('extension.webClipper.action.clip')}
Expand Down
5 changes: 4 additions & 1 deletion apps/extension/src/entrypoints/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Default Popup Title</title>
<title>The AI Native Creation Engine · Refly</title>
<meta name="manifest.type" content="browser_action" />
<meta
name="description"
content="Refly is a free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and WYSIWYG AI editor, easily transforms ideas into quality content." />
</head>
<body>
<div id="root"></div>
Expand Down
29 changes: 28 additions & 1 deletion apps/extension/src/hooks/use-save-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { getMarkdown, getReadabilityMarkdown } from '@refly/utils/html2md';
import getClient from '@refly-packages/ai-workspace-common/requests/proxiedRequest';
import { getClientOrigin } from '@refly/utils/url';
import { getRuntime } from '@refly/utils/env';
import { ConnectionError } from '@refly/errors';
import { ConnectionError, ContentTooLargeError, PayloadTooLargeError } from '@refly/errors';

// Maximum content length (100k characters)
const MAX_CONTENT_LENGTH = 100000;
// Maximum payload size (100KB)
const MAX_PAYLOAD_SIZE_BYTES = 100 * 1024;

export const useSaveCurrentWeblinkAsResource = () => {
const saveResource = async () => {
Expand All @@ -13,6 +18,17 @@ export const useSaveCurrentWeblinkAsResource = () => {
const pageContent = isWeb
? getMarkdown(document?.body)
: getReadabilityMarkdown(document?.body ? document?.body : document);

// Check content length
if (pageContent?.length > MAX_CONTENT_LENGTH) {
return {
url: '',
res: {
errCode: new ContentTooLargeError().code,
} as BaseResponse,
};
}

const resource = {
resourceId: 'tempResId',
title: document?.title || '',
Expand All @@ -36,6 +52,17 @@ export const useSaveCurrentWeblinkAsResource = () => {
},
};

// Check payload size
const payloadSize = new Blob([JSON.stringify(createResourceData)]).size;
if (payloadSize > MAX_PAYLOAD_SIZE_BYTES) {
return {
url: '',
res: {
errCode: new PayloadTooLargeError().code,
} as BaseResponse,
};
}

const { error } = await getClient().createResource(createResourceData);
// const resourceId = data?.data?.resourceId;
// const url = `${getClientOrigin(false)}/resource/${resourceId}`;
Expand Down
28 changes: 27 additions & 1 deletion apps/extension/src/hooks/use-save-selected-content.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { UpsertResourceRequest, type BaseResponse } from '@refly/openapi-schema';
import getClient from '@refly-packages/ai-workspace-common/requests/proxiedRequest';
import { getClientOrigin } from '@refly/utils/url';
import { ConnectionError } from '@refly/errors';
import { ConnectionError, ContentTooLargeError, PayloadTooLargeError } from '@refly/errors';

// Maximum content length (100k characters)
const MAX_CONTENT_LENGTH = 100000;
// Maximum payload size (100KB)
const MAX_PAYLOAD_SIZE_BYTES = 100 * 1024;

interface SaveContentMetadata {
title?: string;
Expand All @@ -19,6 +24,16 @@ export const useSaveSelectedContent = () => {
const title = metadata?.title || document?.title || 'Untitled';
const url = metadata?.url || document?.location?.href || 'https://www.refly.ai';

// Check content length
if (content?.length > MAX_CONTENT_LENGTH) {
return {
url: '',
res: {
errCode: new ContentTooLargeError().code,
} as BaseResponse,
};
}

const createResourceData: UpsertResourceRequest = {
resourceType: 'text',
title,
Expand All @@ -29,6 +44,17 @@ export const useSaveSelectedContent = () => {
},
};

// Check payload size
const payloadSize = new Blob([JSON.stringify(createResourceData)]).size;
if (payloadSize > MAX_PAYLOAD_SIZE_BYTES) {
return {
url: '',
res: {
errCode: new PayloadTooLargeError().code,
} as BaseResponse,
};
}

const { error } = await getClient().createResource({
body: createResourceData,
});
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Name of the extension."
},
"description": {
"message": "A free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and WYSIWYG AI editor, easily transforms ideas into quality content.",
"message": "An OpenSource AI-powered free-form canvas with multi-threaded dialogue, knowledge base, intelligent search, and WYSIWYG AI editor",
"description": "Description of the extension."
},
"displayName": {
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/public/_locales/en_AU/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Name of the extension."
},
"description": {
"message": "A free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and WYSIWYG AI editor, easily transforms ideas into quality content.",
"message": "An OpenSource AI-powered free-form canvas with multi-threaded dialogue, knowledge base, intelligent search, and WYSIWYG AI editor",
"description": "Description of the extension."
},
"displayName": {
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/public/_locales/en_GB/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Name of the extension."
},
"description": {
"message": "A free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and WYSIWYG AI editor, easily transforms ideas into quality content.",
"message": "An OpenSource AI-powered free-form canvas with multi-threaded dialogue, knowledge base, intelligent search, and WYSIWYG AI editor",
"description": "Description of the extension."
},
"displayName": {
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/public/_locales/en_US/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Name of the extension."
},
"description": {
"message": "A free-form canvas creation platform powered by multi-threaded dialogue, knowledge integration, context memory, intelligent search and WYSIWYG AI editor, easily transforms ideas into quality content.",
"message": "An OpenSource AI-powered free-form canvas with multi-threaded dialogue, knowledge base, intelligent search, and WYSIWYG AI editor",
"description": "Description of the extension."
},
"displayName": {
Expand Down
15 changes: 0 additions & 15 deletions apps/extension/src/public/wxt.svg

This file was deleted.

10 changes: 2 additions & 8 deletions apps/extension/wxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,15 @@ export default defineConfig({
},
}) as WxtViteConfig,
manifest: {
version: '0.1.0',
author: 'pftom',
version: '0.3.6',
author: '[email protected]',
name: '__MSG_displayName__',
description: '__MSG_description__',
default_locale: 'en',
host_permissions: ['https://*/*', 'http://*/*', '<all_urls>'],
content_security_policy: {
extension_pages: "script-src 'self'; object-src 'self'",
},
commands: {
_execute_action: {
suggested_key: { default: 'Ctrl+J', mac: 'Command+J' },
},
},
key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlkdw0WXN0WT9YYu1nsWezZzSmWrGpny4gK0UhiL7nbz2NQkqq32KsW51Ag3wdvD/ccyS5VUUEnnlAwxmk0CfnO+TNEFM5lCtF+1/2j5HpmlqZMUlu3tUx+SiY3mF6R9cpbfts3IjWomuRVMfHXmWEu3Gctv4T5hSTNKd44Z3SOPj5KeUxYryJmL/y8LR6lj9F/a5Gfblf5t214GKeFXjewgQOmAGT+v5NurIu3xuwPkYqmkrNcRrQHqkdREH4AFp4TjlNpx5W+AR6Qh9FRkGjXTlcVMQ62KqPlIV29Y/VTO/4oUVhPMhVxXH91ojoA7Vzgr76OtnjaysNZbBapxgFQIDAQAB',
externally_connectable: {
matches: [
Expand All @@ -95,7 +90,6 @@ export default defineConfig({
permissions: [
'storage',
'scripting',
'history',
'activeTab',
'tabs',
'cookies',
Expand Down
14 changes: 14 additions & 0 deletions apps/web/src/components/landing-page-partials/FeaturesBlocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { AiOutlineAppstore, AiOutlineExperiment } from 'react-icons/ai';
import { FaRegPaperPlane } from 'react-icons/fa';
import { HiOutlineDocumentDownload } from 'react-icons/hi';
import { LuSearch } from 'react-icons/lu';
import { MdOutlineNoteAlt } from 'react-icons/md';

Expand Down Expand Up @@ -143,6 +144,19 @@ function FeaturesBlocks() {
color: '#3B82F6',
tagShadow: '0 2px 4px 0 rgba(0,0,0,0.10), inset 0 -4px 0 0 rgba(59,130,246,0.20)',
},
{
tag: t('landingPage.features.featureFive.tag'),
tagIcon: <HiOutlineDocumentDownload className="inline-block" />,
title: t('landingPage.features.featureFive.title'),
bulletPoints: t('landingPage.features.featureFive.bulletPoints', {
returnObjects: true,
}) as string[],
imageSrc: 'https://static.refly.ai/landing/features-clip.webp',
isReversed: false,
background: 'linear-gradient(180deg, #E8F5E9 0%, #FFFFFF 100%)',
color: '#4CAF50',
tagShadow: '0 2px 4px 0 rgba(0,0,0,0.10), inset 0 -4px 0 0 rgba(76,175,80,0.20)',
},
{
tag: t('landingPage.features.featureThree.tag'),
tagIcon: <AiOutlineExperiment className="inline-block" />,
Expand Down
32 changes: 32 additions & 0 deletions apps/web/src/components/landing-page-partials/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
IconLanguage,
} from '@refly-packages/ai-workspace-common/components/common/icon';
import { FaGithub } from 'react-icons/fa6';
import { EXTENSION_DOWNLOAD_LINK, getClientOrigin } from '@refly/utils/url';

const resources = [
{
Expand All @@ -34,6 +35,17 @@ const resources = [
},
];

const platforms = [
{
title: 'chrome',
link: EXTENSION_DOWNLOAD_LINK,
},
{
title: 'web',
link: getClientOrigin(),
},
];

function Footer() {
const { t } = useTranslation();
const { setLoginModalOpen } = useAuthStoreShallow((state) => ({
Expand Down Expand Up @@ -245,6 +257,26 @@ function Footer() {
</ul>
</div>

{/* Platforms Section */}
<div>
<h6 className="mb-1 text-[14px] font-medium">
{t('landingPage.footer.platforms.title')}
</h6>
<ul className="list-none text-sm">
{platforms.map((item) => (
<li key={item.title} className="mb-1">
<Link
to={item.link}
target="_blank"
className="text-gray-500 no-underline transition duration-150 ease-in-out hover:text-gray-700"
>
{t(`landingPage.footer.platforms.${item.title}`)}
</Link>
</li>
))}
</ul>
</div>

{/* Contact Us Section */}
<div>
<h6 className="mb-1 text-[14px] font-medium">
Expand Down
Loading

0 comments on commit 5efc901

Please sign in to comment.