chore: 清理macOS同步产生的重复文件

详细说明:
- 删除了352个带数字后缀的重复文件
- 更新.gitignore防止未来产生此类文件
- 这些文件是由iCloud或其他同步服务冲突产生的
- 不影响项目功能,仅清理冗余文件
This commit is contained in:
Yep_Q
2025-09-08 12:06:01 +08:00
parent 1564396449
commit d6f48d6d14
365 changed files with 2039 additions and 68301 deletions

View File

@@ -1,31 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Auto-generated files
src/components.d.ts

View File

@@ -1,5 +0,0 @@
{
"yarn": false,
"tests": false,
"contents": "./dist"
}

View File

@@ -1,289 +0,0 @@
# n8n Chat
This is an embeddable Chat widget for n8n. It allows the execution of AI-Powered Workflows through a Chat window.
**Windowed Example**
![n8n Chat Windowed](https://raw.githubusercontent.com/n8n-io/n8n/master/packages/frontend/%40n8n/chat/resources/images/windowed.png)
**Fullscreen Example**
![n8n Chat Fullscreen](https://raw.githubusercontent.com/n8n-io/n8n/master/packages/frontend/%40n8n/chat/resources/images/fullscreen.png)
## Prerequisites
Create a n8n workflow which you want to execute via chat. The workflow has to be triggered using a **Chat Trigger** node.
Open the **Chat Trigger** node and add your domain to the **Allowed Origins (CORS)** field. This makes sure that only requests from your domain are accepted.
[See example workflow](https://github.com/n8n-io/n8n/blob/master/packages/%40n8n/chat/resources/workflow.json)
To use streaming responses, you need to enable the **Streaming response** response mode in the **Chat Trigger** node.
[See example workflow with streaming](https://github.com/n8n-io/n8n/blob/master/packages/%40n8n/chat/resources/workflow-streaming.json)
> Make sure the workflow is **Active.**
### How it works
Each Chat request is sent to the n8n Webhook endpoint, which then sends back a response.
Each request is accompanied by an `action` query parameter, where `action` can be one of:
- `loadPreviousSession` - When the user opens the Chatbot again and the previous chat session should be loaded
- `sendMessage` - When the user sends a message
## Installation
Open the **Webhook** node and replace `YOUR_PRODUCTION_WEBHOOK_URL` with your production URL. This is the URL that the Chat widget will use to send requests to.
### a. CDN Embed
Add the following code to your HTML page.
```html
<link href="https://cdn.jsdelivr.net/npm/@n8n/chat/dist/style.css" rel="stylesheet" />
<script type="module">
import { createChat } from 'https://cdn.jsdelivr.net/npm/@n8n/chat/dist/chat.bundle.es.js';
createChat({
webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL'
});
</script>
```
### b. Import Embed
Install and save n8n Chat as a production dependency.
```sh
npm install @n8n/chat
```
Import the CSS and use the `createChat` function to initialize your Chat window.
```ts
import '@n8n/chat/style.css';
import { createChat } from '@n8n/chat';
createChat({
webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL'
});
```
##### Vue.js
```html
<script lang="ts" setup>
// App.vue
import { onMounted } from 'vue';
import '@n8n/chat/style.css';
import { createChat } from '@n8n/chat';
onMounted(() => {
createChat({
webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL'
});
});
</script>
<template>
<div></div>
</template>
```
##### React
```tsx
// App.tsx
import { useEffect } from 'react';
import '@n8n/chat/style.css';
import { createChat } from '@n8n/chat';
export const App = () => {
useEffect(() => {
createChat({
webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL'
});
}, []);
return (<div></div>);
};
```
## Options
The default options are:
```ts
createChat({
webhookUrl: '',
webhookConfig: {
method: 'POST',
headers: {}
},
target: '#n8n-chat',
mode: 'window',
chatInputKey: 'chatInput',
chatSessionKey: 'sessionId',
loadPreviousSession: true,
metadata: {},
showWelcomeScreen: false,
defaultLanguage: 'en',
initialMessages: [
'Hi there! 👋',
'My name is Nathan. How can I assist you today?'
],
i18n: {
en: {
title: 'Hi there! 👋',
subtitle: "Start a chat. We're here to help you 24/7.",
footer: '',
getStarted: 'New Conversation',
inputPlaceholder: 'Type your question..',
},
},
enableStreaming: false,
});
```
### `webhookUrl`
- **Type**: `string`
- **Required**: `true`
- **Examples**:
- `https://yourname.app.n8n.cloud/webhook/513107b3-6f3a-4a1e-af21-659f0ed14183`
- `http://localhost:5678/webhook/513107b3-6f3a-4a1e-af21-659f0ed14183`
- **Description**: The URL of the n8n Webhook endpoint. Should be the production URL.
### `webhookConfig`
- **Type**: `{ method: string, headers: Record<string, string> }`
- **Default**: `{ method: 'POST', headers: {} }`
- **Description**: The configuration for the Webhook request.
### `target`
- **Type**: `string`
- **Default**: `'#n8n-chat'`
- **Description**: The CSS selector of the element where the Chat window should be embedded.
### `mode`
- **Type**: `'window' | 'fullscreen'`
- **Default**: `'window'`
- **Description**: The render mode of the Chat window.
- In `window` mode, the Chat window will be embedded in the target element as a chat toggle button and a fixed size chat window.
- In `fullscreen` mode, the Chat will take up the entire width and height of its target container.
### `showWelcomeScreen`
- **Type**: `boolean`
- **Default**: `false`
- **Description**: Whether to show the welcome screen when the Chat window is opened.
### `chatInputKey`
- **Type**: `string`
- **Default**: `'chatInput'`
- **Description**: The key to use for sending the chat input for the AI Agent node.
### `chatSessionKey`
- **Type**: `string`
- **Default**: `'sessionId'`
- **Description**: The key to use for sending the chat history session ID for the AI Memory node.
### `loadPreviousSession`
- **Type**: `boolean`
- **Default**: `true`
- **Description**: Whether to load previous messages (chat context).
### `defaultLanguage`
- **Type**: `string`
- **Default**: `'en'`
- **Description**: The default language of the Chat window. Currently only `en` is supported.
### `i18n`
- **Type**: `{ [key: string]: Record<string, string> }`
- **Description**: The i18n configuration for the Chat window. Currently only `en` is supported.
### `initialMessages`
- **Type**: `string[]`
- **Description**: The initial messages to be displayed in the Chat window.
### `allowFileUploads`
- **Type**: `Ref<boolean> | boolean`
- **Default**: `false`
- **Description**: Whether to allow file uploads in the chat. If set to `true`, users will be able to upload files through the chat interface.
### `allowedFilesMimeTypes`
- **Type**: `Ref<string> | string`
- **Default**: `''`
- **Description**: A comma-separated list of allowed MIME types for file uploads. Only applicable if `allowFileUploads` is set to `true`. If left empty, all file types are allowed. For example: `'image/*,application/pdf'`.
### enableStreaming
- Type: boolean
- Default: false
- Description: Whether to enable streaming responses from the n8n workflow. If set to `true`, the chat will display responses as they are being generated, providing a more interactive experience. For this to work the workflow must be configured as well to return streaming responses.
## Customization
The Chat window is entirely customizable using CSS variables.
```css
:root {
--chat--color-primary: #e74266;
--chat--color-primary-shade-50: #db4061;
--chat--color-primary-shade-100: #cf3c5c;
--chat--color-secondary: #20b69e;
--chat--color-secondary-shade-50: #1ca08a;
--chat--color-white: #ffffff;
--chat--color-light: #f2f4f8;
--chat--color-light-shade-50: #e6e9f1;
--chat--color-light-shade-100: #c2c5cc;
--chat--color-medium: #d2d4d9;
--chat--color-dark: #101330;
--chat--color-disabled: #777980;
--chat--color-typing: #404040;
--chat--spacing: 1rem;
--chat--border-radius: 0.25rem;
--chat--transition-duration: 0.15s;
--chat--window--width: 400px;
--chat--window--height: 600px;
--chat--header-height: auto;
--chat--header--padding: var(--chat--spacing);
--chat--header--background: var(--chat--color-dark);
--chat--header--color: var(--chat--color-light);
--chat--header--border-top: none;
--chat--header--border-bottom: none;
--chat--header--border-bottom: none;
--chat--header--border-bottom: none;
--chat--heading--font-size: 2em;
--chat--header--color: var(--chat--color-light);
--chat--subtitle--font-size: inherit;
--chat--subtitle--line-height: 1.8;
--chat--textarea--height: 50px;
--chat--message--font-size: 1rem;
--chat--message--padding: var(--chat--spacing);
--chat--message--border-radius: var(--chat--border-radius);
--chat--message-line-height: 1.8;
--chat--message--bot--background: var(--chat--color-white);
--chat--message--bot--color: var(--chat--color-dark);
--chat--message--bot--border: none;
--chat--message--user--background: var(--chat--color-secondary);
--chat--message--user--color: var(--chat--color-white);
--chat--message--user--border: none;
--chat--message--pre--background: rgba(0, 0, 0, 0.05);
--chat--toggle--background: var(--chat--color-primary);
--chat--toggle--hover--background: var(--chat--color-primary-shade-50);
--chat--toggle--active--background: var(--chat--color-primary-shade-100);
--chat--toggle--color: var(--chat--color-white);
--chat--toggle--size: 64px;
}
```
## Caveats
### Fullscreen mode
In fullscreen mode, the Chat window will take up the entire width and height of its target container. Make sure that the container has a set width and height.
```css
html,
body,
#n8n-chat {
width: 100%;
height: 100%;
}
```
## License
You can find the license information [here](https://github.com/n8n-io/n8n/blob/master/README.md#license)

View File

@@ -1,17 +0,0 @@
import { frontendConfig } from '@n8n/eslint-config/frontend';
import { defineConfig } from 'eslint/config';
export default defineConfig(frontendConfig, {
rules: {
// TODO: Remove these
'no-empty': 'warn',
'@typescript-eslint/require-await': 'warn',
'@typescript-eslint/no-empty-object-type': 'warn',
'@typescript-eslint/naming-convention': 'warn',
'@typescript-eslint/no-unsafe-function-type': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'warn',
},
});

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,68 +0,0 @@
{
"name": "@n8n/chat",
"version": "0.54.0",
"scripts": {
"dev": "pnpm run storybook",
"build": "pnpm build:vite && pnpm build:bundle",
"build:vite": "cross-env vite build",
"build:bundle": "cross-env INCLUDE_VUE=true vite build",
"preview": "vite preview",
"test:dev": "vitest",
"test": "vitest run",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint src --quiet",
"lint:fix": "eslint src --fix",
"lint:styles": "stylelint \"src/**/*.{scss,sass,vue}\" --cache",
"lint:styles:fix": "stylelint \"src/**/*.{scss,sass,vue}\" --fix --cache",
"format": "biome format --write src .storybook && prettier --write src/ --ignore-path ../../../../.prettierignore",
"format:check": "biome ci src .storybook && prettier --check src/ --ignore-path ../../../../.prettierignore",
"storybook": "storybook dev -p 6006 --no-open",
"build:storybook": "storybook build"
},
"types": "./dist/index.d.ts",
"main": "./dist/chat.umd.js",
"module": "./dist/chat.es.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/chat.es.js",
"require": "./dist/chat.umd.js"
},
"./style.css": {
"import": "./dist/style.css",
"require": "./dist/style.css"
},
"./*": {
"import": "./*",
"require": "./*"
}
},
"dependencies": {
"@n8n/design-system": "workspace:*",
"@vueuse/core": "catalog:frontend",
"highlight.js": "catalog:frontend",
"markdown-it-link-attributes": "^4.0.1",
"uuid": "catalog:",
"vue": "catalog:frontend",
"vue-markdown-render": "catalog:frontend"
},
"devDependencies": {
"@iconify-json/mdi": "^1.1.54",
"@n8n/storybook": "workspace:*",
"@n8n/eslint-config": "workspace:*",
"@n8n/stylelint-config": "workspace:*",
"@n8n/typescript-config": "workspace:*",
"@n8n/vitest-config": "workspace:*",
"@vitejs/plugin-vue": "catalog:frontend",
"@vitest/coverage-v8": "catalog:",
"unplugin-icons": "^0.19.0",
"vite": "catalog:",
"vitest": "catalog:",
"vite-plugin-dts": "^4.5.3",
"vue-tsc": "catalog:frontend"
},
"files": [
"README.md",
"dist"
]
}

View File

@@ -1,3 +0,0 @@
import { baseConfig } from '@n8n/stylelint-config/base';
export default baseConfig;

View File

@@ -1,25 +0,0 @@
{
"extends": "@n8n/typescript-config/tsconfig.common.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"baseUrl": "src",
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"allowJs": true,
"importHelpers": true,
"incremental": false,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"types": ["vitest/globals"],
"paths": {
"@n8n/chat/*": ["./*"],
"@n8n/design-system*": ["../../design-system/src*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
// TODO: remove all options below this line
"useUnknownInCatchVariables": false
},
"include": ["**/*.ts", "**/*.vue"]
}

View File

@@ -1,119 +0,0 @@
import { defineConfig, mergeConfig, PluginOption } from 'vite';
import { resolve } from 'path';
import { renameSync, writeFileSync, readFileSync } from 'fs';
import vue from '@vitejs/plugin-vue';
import icons from 'unplugin-icons/vite';
import dts from 'vite-plugin-dts';
import { vitestConfig } from '@n8n/vitest-config/frontend';
import pkg from './package.json';
import iconsResolver from 'unplugin-icons/resolver';
import components from 'unplugin-vue-components/vite';
const includeVue = process.env.INCLUDE_VUE === 'true';
const srcPath = resolve(__dirname, 'src');
const packagesDir = resolve(__dirname, '..', '..', '..');
const banner = `/*! Package version @n8n/chat@${pkg.version} */`;
// https://vitejs.dev/config/
export default mergeConfig(
defineConfig({
plugins: [
vue(),
icons({
compiler: 'vue3',
autoInstall: true,
}),
dts(),
components({
dts: './src/components.d.ts',
resolvers: [
(componentName) => {
if (componentName.startsWith('N8n'))
return { name: componentName, from: '@n8n/design-system' };
},
iconsResolver({
prefix: 'icon',
}),
],
}),
{
name: 'rename-css-file',
closeBundle() {
// The chat.css is automatically named based on vite.config.ts library name.
// ChatTrigger Node requires https://cdn.jsdelivr.net/npm/@n8n/chat/dist/style.css
// As such for backwards compatibility, we need to maintain the same name file
const cssPath = resolve(__dirname, 'dist', 'chat.css');
const newCssPath = resolve(__dirname, 'dist', 'style.css');
try {
renameSync(cssPath, newCssPath);
} catch (error) {
console.error('Failed to rename chat.css file:', error);
}
},
},
{
name: 'inject-build-version',
closeBundle() {
const cssPath = resolve(__dirname, 'dist', 'style.css');
try {
const cssContent = readFileSync(cssPath, 'utf-8');
const updatedCssContent = banner + '\n' + cssContent;
writeFileSync(cssPath, updatedCssContent, 'utf-8');
} catch (error) {
console.error('Failed to inject build version into CSS file:', error);
}
},
},
],
resolve: {
alias: [
{
find: '@',
replacement: srcPath,
},
{
find: '@n8n/chat',
replacement: srcPath,
},
{
find: /^@n8n\/chat(.+)$/,
replacement: srcPath + '$1',
},
{
find: /^@n8n\/design-system(.+)$/,
replacement: resolve(packagesDir, 'frontend', '@n8n', 'design-system', 'src$1'),
},
],
},
define: {
'process.env.NODE_ENV': process.env.NODE_ENV ? `"${process.env.NODE_ENV}"` : '"development"',
},
build: {
emptyOutDir: !includeVue,
lib: {
entry: resolve(__dirname, 'src', 'index.ts'),
name: 'N8nChat',
fileName: (format) => (includeVue ? `chat.bundle.${format}.js` : `chat.${format}.js`),
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: includeVue ? [] : ['vue'],
output: {
exports: 'named',
// inject banner on top of all JS files
banner,
// Provide global variables to use in the UMD build
// for externalized deps
globals: includeVue
? {}
: {
vue: 'Vue',
},
},
},
},
}),
vitestConfig,
);