feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
@@ -0,0 +1,31 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
|
||||
/** @type { import('@storybook/react-vite').StorybookConfig } */
|
||||
const config = {
|
||||
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.tsx'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-onboarding',
|
||||
'@storybook/addon-interactions',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
viteFinal: config =>
|
||||
mergeConfig(config, {
|
||||
plugins: [
|
||||
svgr({
|
||||
svgrOptions: {
|
||||
native: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,14 @@
|
||||
/** @type { import('@storybook/react').Preview } */
|
||||
const preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
@@ -0,0 +1,5 @@
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
});
|
||||
16
frontend/packages/common/prompt-kit/adapter/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# @coze-common/prompt-kit-adapter
|
||||
|
||||
> Project template for react component with storybook.
|
||||
|
||||
## Features
|
||||
|
||||
- [x] eslint & ts
|
||||
- [x] esm bundle
|
||||
- [x] umd bundle
|
||||
- [x] storybook
|
||||
|
||||
## Commands
|
||||
|
||||
- init: `rush update`
|
||||
- dev: `npm run dev`
|
||||
- build: `npm run build`
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {},
|
||||
});
|
||||
55
frontend/packages/common/prompt-kit/adapter/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@coze-common/prompt-kit-adapter",
|
||||
"version": "0.0.1",
|
||||
"description": "@coze-common/prompt-kit-adapter",
|
||||
"license": "Apache-2.0",
|
||||
"author": "haozhenfei@bytedance.com",
|
||||
"maintainers": [],
|
||||
"exports": {
|
||||
".": "./src/index.tsx",
|
||||
"./create-prompt": "./src/create-prompt/index.tsx"
|
||||
},
|
||||
"main": "src/index.tsx",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"create-prompt": [
|
||||
"./src/create-prompt/index.tsx"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "npm run test -- --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-common/editor-plugins": "workspace:*",
|
||||
"@coze-common/prompt-kit-base": "workspace:*",
|
||||
"classnames": "^2.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"vite-plugin-svgr": "~3.3.0",
|
||||
"vitest": "~3.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.2.0",
|
||||
"react-dom": ">=18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { usePromptConfiguratorModal } from './use-modal';
|
||||
export { PromptConfiguratorModal } from './prompt-configurator-modal';
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useCursorInInputSlot } from '@coze-common/editor-plugins/input-slot';
|
||||
import {
|
||||
PromptConfiguratorModal as BasePromptConfiguratorModal,
|
||||
type PromptConfiguratorModalProps,
|
||||
ImportPromptWhenEmptyPlaceholder,
|
||||
useCreatePromptContext,
|
||||
InsertInputSlotButton,
|
||||
} from '@coze-common/prompt-kit-base/create-prompt';
|
||||
|
||||
export { usePromptConfiguratorModal } from '@coze-common/prompt-kit-base/create-prompt';
|
||||
|
||||
export const PromptConfiguratorModal = (
|
||||
props: PromptConfiguratorModalProps,
|
||||
) => {
|
||||
const { isReadOnly } = useCreatePromptContext() || {};
|
||||
const inInputSlot = useCursorInInputSlot();
|
||||
return (
|
||||
<BasePromptConfiguratorModal
|
||||
{...props}
|
||||
promptSectionConfig={{
|
||||
editorPlaceholder: <ImportPromptWhenEmptyPlaceholder />,
|
||||
editorActions: <InsertInputSlotButton disabled={inInputSlot} />,
|
||||
headerActions: !isReadOnly ? (
|
||||
<div className="flex gap-2">
|
||||
<div>
|
||||
<InsertInputSlotButton disabled={inInputSlot} />
|
||||
</div>
|
||||
</div>
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
usePromptConfiguratorModal as BaseUsePromptConfiguratorModal,
|
||||
type UsePromptConfiguratorModalProps,
|
||||
} from '@coze-common/prompt-kit-base/create-prompt';
|
||||
|
||||
import { PromptConfiguratorModal } from './prompt-configurator-modal';
|
||||
|
||||
export const usePromptConfiguratorModal = (
|
||||
props: UsePromptConfiguratorModalProps,
|
||||
) =>
|
||||
BaseUsePromptConfiguratorModal({
|
||||
...props,
|
||||
CustomPromptConfiguratorModal: PromptConfiguratorModal,
|
||||
});
|
||||
17
frontend/packages/common/prompt-kit/adapter/src/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { usePromptConfiguratorModal } from './create-prompt';
|
||||
17
frontend/packages/common/prompt-kit/adapter/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DemoComponent } from '../src';
|
||||
|
||||
export default {
|
||||
title: 'Example/Demo',
|
||||
component: DemoComponent,
|
||||
parameters: {
|
||||
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
|
||||
layout: 'centered',
|
||||
},
|
||||
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
|
||||
tags: ['autodocs'],
|
||||
// More on argTypes: https://storybook.js.org/docs/api/argtypes
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
||||
export const Base = {
|
||||
args: {
|
||||
name: 'tecvan',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Meta } from "@storybook/blocks";
|
||||
|
||||
<Meta title="Hello world" />
|
||||
|
||||
<div className="sb-container">
|
||||
<div className='sb-section-title'>
|
||||
# Hello world
|
||||
|
||||
Hello world
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
{`
|
||||
.sb-container {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.sb-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.sb-section-title {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../arch/i18n/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../base/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/eslint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/stylelint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/ts-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/vitest-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../editor-plugins/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/common/prompt-kit/adapter/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"exclude": ["**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist",
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": ["__tests__", "vitest.config.ts", "stories"],
|
||||
"exclude": ["./dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
22
frontend/packages/common/prompt-kit/adapter/vitest.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineConfig } from '@coze-arch/vitest-config';
|
||||
|
||||
export default defineConfig({
|
||||
dirname: __dirname,
|
||||
preset: 'web',
|
||||
});
|
||||
31
frontend/packages/common/prompt-kit/base/.storybook/main.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
|
||||
/** @type { import('@storybook/react-vite').StorybookConfig } */
|
||||
const config = {
|
||||
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.tsx'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-onboarding',
|
||||
'@storybook/addon-interactions',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
viteFinal: config =>
|
||||
mergeConfig(config, {
|
||||
plugins: [
|
||||
svgr({
|
||||
svgrOptions: {
|
||||
native: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,14 @@
|
||||
/** @type { import('@storybook/react').Preview } */
|
||||
const preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
5
frontend/packages/common/prompt-kit/base/.stylelintrc.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
});
|
||||
16
frontend/packages/common/prompt-kit/base/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# @coze-common/prompt-kit-base
|
||||
|
||||
> Project template for react component with storybook.
|
||||
|
||||
## Features
|
||||
|
||||
- [x] eslint & ts
|
||||
- [x] esm bundle
|
||||
- [x] umd bundle
|
||||
- [x] storybook
|
||||
|
||||
## Commands
|
||||
|
||||
- init: `rush update`
|
||||
- dev: `npm run dev`
|
||||
- build: `npm run build`
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {},
|
||||
});
|
||||
81
frontend/packages/common/prompt-kit/base/package.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "@coze-common/prompt-kit-base",
|
||||
"version": "0.0.1",
|
||||
"description": "@coze-common/prompt-kit-base",
|
||||
"license": "Apache-2.0",
|
||||
"author": "haozhenfei@bytedance.com",
|
||||
"maintainers": [],
|
||||
"exports": {
|
||||
"./create-prompt": "./src/create-prompt/index.tsx",
|
||||
"./shared": "./src/shared/index.tsx",
|
||||
"./shared/types": "./src/shared/types/index.ts",
|
||||
"./shared/css": "./src/shared/css/index.css",
|
||||
"./editor": "./src/editor/index.tsx"
|
||||
},
|
||||
"main": "src/index.tsx",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"create-prompt": [
|
||||
"./src/create-prompt/index.tsx"
|
||||
],
|
||||
"shared": [
|
||||
"./src/shared/index.tsx"
|
||||
],
|
||||
"editor": [
|
||||
"./src/editor/index.tsx"
|
||||
],
|
||||
"shared/css": [
|
||||
"./src/shared/css/index.css"
|
||||
],
|
||||
"shared/types": [
|
||||
"./src/shared/types/index.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "npm run test -- --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/view": "^6.34.1",
|
||||
"@coze-arch/bot-api": "workspace:*",
|
||||
"@coze-arch/bot-tea": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-common/editor-plugins": "workspace:*",
|
||||
"@coze-editor/editor": "0.1.0-alpha.d92d50",
|
||||
"@coze-foundation/local-storage": "workspace:*",
|
||||
"ahooks": "^3.7.8",
|
||||
"classnames": "^2.3.2",
|
||||
"immer": "^10.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react-markdown": "^8.0.3",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"vite-plugin-svgr": "~3.3.0",
|
||||
"vitest": "~3.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.2.0",
|
||||
"react-dom": ">=18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,145 @@
|
||||
<svg width="356" height="178" viewBox="0 0 356 178" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.2">
|
||||
<mask id="mask0_7107_191040" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="356" height="178">
|
||||
<rect x="0.540771" width="354.918" height="177.459" fill="url(#paint0_linear_7107_191040)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_7107_191040)">
|
||||
<mask id="mask1_7107_191040" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="356" height="178">
|
||||
<rect x="0.540771" width="354.918" height="177.459" fill="url(#paint1_linear_7107_191040)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_7107_191040)">
|
||||
<rect x="0.540771" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" width="29.5765" height="29.5765" fill="url(#paint2_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="29.5767" width="29.5765" height="29.5765" fill="url(#paint3_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="59.1528" width="29.5765" height="29.5765" fill="url(#paint4_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="118.306" width="29.5765" height="29.5765" fill="url(#paint5_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="147.883" width="29.5765" height="29.5765" fill="url(#paint6_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<rect x="80.7778" y="82.3135" width="194.444" height="70.8333" rx="5.55556" fill="#F9F9F9"/>
|
||||
<rect x="81.1251" y="82.6607" width="193.75" height="70.1389" rx="5.20833" stroke="url(#paint7_linear_7107_191040)" stroke-opacity="0.24" stroke-width="0.694444"/>
|
||||
<rect opacity="0.12" x="91.8889" y="94.814" width="72.2222" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<rect opacity="0.12" x="91.8889" y="110.092" width="136.111" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<g filter="url(#filter0_d_7107_191040)">
|
||||
<path d="M236.333 97.8693C236.333 96.3135 236.333 95.5357 236.636 94.9415C236.902 94.4188 237.327 93.9939 237.85 93.7276C238.444 93.4248 239.222 93.4248 240.778 93.4248H259.667C261.222 93.4248 262 93.4248 262.594 93.7276C263.117 93.9939 263.542 94.4188 263.808 94.9415C264.111 95.5357 264.111 96.3135 264.111 97.8692V116.758C264.111 118.314 264.111 119.092 263.808 119.686C263.542 120.209 263.117 120.634 262.594 120.9C262 121.203 261.222 121.203 259.667 121.203H240.778C239.222 121.203 238.444 121.203 237.85 120.9C237.327 120.634 236.902 120.209 236.636 119.686C236.333 119.092 236.333 118.314 236.333 116.758V97.8693Z" fill="url(#paint8_linear_7107_191040)"/>
|
||||
<path d="M236.403 97.8693C236.403 97.0903 236.403 96.5102 236.44 96.0505C236.478 95.5916 236.552 95.2587 236.698 94.973C236.958 94.4634 237.372 94.0491 237.881 93.7894C238.167 93.6439 238.5 93.5694 238.959 93.5319C239.419 93.4943 239.999 93.4942 240.778 93.4942H259.667C260.446 93.4942 261.026 93.4943 261.485 93.5319C261.944 93.5694 262.277 93.6439 262.563 93.7894C263.072 94.0491 263.487 94.4634 263.746 94.973C263.892 95.2587 263.966 95.5916 264.004 96.0505C264.042 96.5102 264.042 97.0903 264.042 97.8692V116.758C264.042 117.537 264.042 118.117 264.004 118.577C263.966 119.036 263.892 119.369 263.746 119.654C263.487 120.164 263.072 120.578 262.563 120.838C262.277 120.984 261.944 121.058 261.485 121.096C261.026 121.133 260.446 121.133 259.667 121.133H240.778C239.999 121.133 239.419 121.133 238.959 121.096C238.5 121.058 238.167 120.984 237.881 120.838C237.372 120.578 236.958 120.164 236.698 119.654C236.552 119.369 236.478 119.036 236.44 118.577C236.403 118.117 236.403 117.537 236.403 116.758V97.8693Z" stroke="black" stroke-opacity="0.08" stroke-width="0.138889"/>
|
||||
</g>
|
||||
<rect opacity="0.12" x="91.8889" y="132.313" width="172.222" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_7107_191040" x="230.381" y="89.4566" width="39.6826" height="39.6826" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1.98413"/>
|
||||
<feGaussianBlur stdDeviation="2.97619"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_7107_191040"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_7107_191040" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_7107_191040" x1="355.459" y1="88.7296" x2="0.540769" y2="88.7296" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
<stop offset="0.500048" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_7107_191040" x1="178" y1="0" x2="178" y2="177.459" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
<stop offset="0.493585" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_7107_191040" x1="44.9055" y1="0" x2="44.9055" y2="29.5765" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_7107_191040" x1="133.635" y1="29.5767" x2="133.635" y2="59.1532" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_7107_191040" x1="222.365" y1="59.1528" x2="222.365" y2="88.7294" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_7107_191040" x1="163.212" y1="118.306" x2="163.212" y2="147.883" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_7107_191040" x1="311.094" y1="147.883" x2="311.094" y2="177.459" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_7107_191040" x1="178" y1="82.3135" x2="178" y2="153.147" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#38415A"/>
|
||||
<stop offset="1" stop-color="#2B3245" stop-opacity="0.4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_7107_191040" x1="250.222" y1="93.4248" x2="250.222" y2="121.203" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F45D68"/>
|
||||
<stop offset="1" stop-color="#FFCA00"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 43 KiB |
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
interface CloseModalProps {
|
||||
onCancel?: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export const CloseModal = ({ onCancel }: CloseModalProps) => (
|
||||
<Button color="primary" onClick={onCancel}>
|
||||
{I18n.t('Cancel')}
|
||||
</Button>
|
||||
);
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { CloseModal } from './close-modal';
|
||||
export { SavePrompt } from './save-prompt';
|
||||
export { PromptDiff } from './prompt-diff';
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
import { sendTeaEvent, EVENT_NAMES } from '@coze-arch/bot-tea';
|
||||
interface PromptDiffProps {
|
||||
mode: 'info' | 'edit' | 'create';
|
||||
editor?: EditorAPI;
|
||||
spaceId: string;
|
||||
botId?: string;
|
||||
projectId?: string;
|
||||
workflowId?: string;
|
||||
source: string;
|
||||
submitFun?: (
|
||||
e: React.MouseEvent<Element, MouseEvent>,
|
||||
) => Promise<{ mode: string; id: string } | undefined>;
|
||||
editId?: string;
|
||||
onDiff?: ({
|
||||
prompt,
|
||||
libraryId,
|
||||
}: {
|
||||
prompt: string;
|
||||
libraryId: string;
|
||||
}) => void;
|
||||
onCancel?: (e: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
export const PromptDiff = ({
|
||||
mode,
|
||||
editor,
|
||||
spaceId,
|
||||
botId,
|
||||
projectId,
|
||||
workflowId,
|
||||
source,
|
||||
submitFun,
|
||||
editId,
|
||||
onDiff,
|
||||
onCancel,
|
||||
}: PromptDiffProps) => {
|
||||
if (mode === 'info') {
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={e => {
|
||||
if (!editId) {
|
||||
return;
|
||||
}
|
||||
onDiff?.({ prompt: editor?.getValue() ?? '', libraryId: editId });
|
||||
sendTeaEvent(EVENT_NAMES.compare_mode_front, {
|
||||
source,
|
||||
space_id: spaceId,
|
||||
action: 'start',
|
||||
compare_type: 'prompts',
|
||||
bot_id: botId,
|
||||
from: 'prompt_resource',
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
onCancel?.(e);
|
||||
}}
|
||||
>
|
||||
{I18n.t('compare_prompt_compare_debug')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={async e => {
|
||||
const res = await submitFun?.(e);
|
||||
if (res?.id) {
|
||||
onDiff?.({ prompt: editor?.getValue() ?? '', libraryId: res.id });
|
||||
sendTeaEvent(EVENT_NAMES.compare_mode_front, {
|
||||
source,
|
||||
space_id: spaceId,
|
||||
action: 'start',
|
||||
compare_type: 'prompts',
|
||||
bot_id: botId,
|
||||
from: 'prompt_resource',
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
}
|
||||
onCancel?.(e);
|
||||
}}
|
||||
>
|
||||
{I18n.t('creat_prompt_button_comfirm_and_compare')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
interface SavePromptProps {
|
||||
mode: 'info' | 'edit' | 'create';
|
||||
isSubmitting?: boolean;
|
||||
onSubmit?: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export const SavePrompt = ({
|
||||
mode,
|
||||
isSubmitting,
|
||||
onSubmit,
|
||||
}: SavePromptProps) => (
|
||||
<Button loading={isSubmitting} onClick={onSubmit}>
|
||||
{mode === 'info' ? I18n.t('prompt_detail_copy_prompt') : I18n.t('Confirm')}
|
||||
</Button>
|
||||
);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozEdit } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip, Divider, IconButton } from '@coze-arch/coze-design';
|
||||
interface PromptHeaderProps {
|
||||
canEdit: boolean;
|
||||
onEditIconClick?: () => void;
|
||||
mode: 'info' | 'edit' | 'create';
|
||||
}
|
||||
export const PromptHeader = ({
|
||||
canEdit,
|
||||
onEditIconClick,
|
||||
mode,
|
||||
}: PromptHeaderProps) => {
|
||||
if (mode === 'info' && canEdit) {
|
||||
return (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span>{I18n.t('prompt_detail_prompt_detail')}</span>
|
||||
<div className="flex items-center ">
|
||||
<Tooltip content={I18n.t('prompt_library_edit')}>
|
||||
<IconButton
|
||||
color="secondary"
|
||||
icon={<IconCozEdit className="semi-icon-default" />}
|
||||
onClick={onEditIconClick}
|
||||
size="small"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider layout="vertical" className="mx-[10px] coz-stroke-primary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (mode === 'create') {
|
||||
return <>{I18n.t('creat_new_prompt')}</>;
|
||||
}
|
||||
|
||||
if (mode === 'edit') {
|
||||
return <>{I18n.t('edit_prompt')}</>;
|
||||
}
|
||||
|
||||
return <>{I18n.t('prompt_detail_prompt_detail')}</>;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useLayoutEffect, type PropsWithChildren } from 'react';
|
||||
|
||||
import { useLocalStorageState } from 'ahooks';
|
||||
import { useEditor, useInjector } from '@coze-editor/editor/react';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { insertInputSlot } from '@coze-common/editor-plugins/actions';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozInputSlot } from '@coze-arch/coze-design/icons';
|
||||
import { Button, Tooltip } from '@coze-arch/coze-design';
|
||||
import { type ButtonProps } from '@coze-arch/coze-design';
|
||||
import { keymap } from '@codemirror/view';
|
||||
|
||||
import { useReadonly } from '../../shared/hooks/use-editor-readonly';
|
||||
import InsertBlankSlotGuideEn from '../../assets/insert-blank-slot-guide-en.png';
|
||||
import InsertBlankSlotGuideCn from '../../assets/insert-blank-slot-guide-cn.png';
|
||||
import BlankSlotShortCutIcon from '../../assets/blank-slot-shortcut-icon.png';
|
||||
type NlPromptActionProps = Pick<ButtonProps, 'className'> & {
|
||||
disabled?: boolean;
|
||||
};
|
||||
export const InsertInputSlotButton: React.FC<NlPromptActionProps> = props => {
|
||||
const { className, disabled } = props;
|
||||
const editor = useEditor<EditorAPI | undefined>();
|
||||
const readonly = useReadonly();
|
||||
const injector = useInjector();
|
||||
const [showActionGuide, setShowActionGuide] = useLocalStorageState(
|
||||
insertInputSlotTooltipGuideKey,
|
||||
{
|
||||
defaultValue: true,
|
||||
},
|
||||
);
|
||||
|
||||
useLayoutEffect(
|
||||
() =>
|
||||
injector.inject([
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Cmd-k',
|
||||
run() {
|
||||
if (!editor || readonly || disabled) {
|
||||
return false;
|
||||
}
|
||||
insertInputSlot(editor);
|
||||
return false;
|
||||
},
|
||||
},
|
||||
]),
|
||||
]),
|
||||
[injector, editor, readonly, disabled],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="hover:coz-mg-secondary-hovered coz-icon-button coz-icon-button-default rounded-little">
|
||||
<GuideTooltip showActionGuide={!!showActionGuide}>
|
||||
<Button
|
||||
color="primary"
|
||||
size="small"
|
||||
disabled={readonly || disabled}
|
||||
icon={<IconCozInputSlot />}
|
||||
className={className}
|
||||
onMouseDown={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!editor || readonly) {
|
||||
return;
|
||||
}
|
||||
setShowActionGuide(false);
|
||||
insertInputSlot(editor);
|
||||
}}
|
||||
>
|
||||
{I18n.t('creat_new_prompt_edit_block')}
|
||||
</Button>
|
||||
</GuideTooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const insertInputSlotTooltipGuideKey = 'insert_input_slot_tooltip_guide';
|
||||
|
||||
const GuideTooltip: React.FC<
|
||||
PropsWithChildren<{
|
||||
showActionGuide: boolean;
|
||||
}>
|
||||
> = ({ showActionGuide, children }) => {
|
||||
if (showActionGuide) {
|
||||
return (
|
||||
<Tooltip
|
||||
content={
|
||||
<div className="flex flex-col">
|
||||
<img
|
||||
className="w-full h-auto"
|
||||
src={
|
||||
IS_CN_REGION ? InsertBlankSlotGuideCn : InsertBlankSlotGuideEn
|
||||
}
|
||||
/>
|
||||
<div className="flex flex-col mt-2 p-2 gap-1">
|
||||
<div className="flex items-center justify-between ">
|
||||
<span className="text-xxl font-medium">
|
||||
{I18n.t('edit_block_guild_title')}
|
||||
</span>
|
||||
<img src={BlankSlotShortCutIcon} className="w-[33px] h-5" />
|
||||
</div>
|
||||
<div className="text-sm coz-fg-primary">
|
||||
{I18n.t('edit_block_guild_describe')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
className="!w-[301px] !max-w-[301px]"
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
content={
|
||||
<div
|
||||
className="coz-fg-primary text-sm"
|
||||
style={{
|
||||
fontFamily: '-apple-system, SF Pro',
|
||||
}}
|
||||
>
|
||||
⌘ K
|
||||
</div>
|
||||
}
|
||||
className="coz-fg-primary text-sm"
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEditor } from '@coze-editor/editor/react';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { useCreatePromptContext } from '@/create-prompt/context';
|
||||
|
||||
export const ImportPromptWhenEmptyPlaceholder = () => {
|
||||
const editor = useEditor<EditorAPI>();
|
||||
const { props, formApiRef } = useCreatePromptContext() || {};
|
||||
const { importPromptWhenEmpty } = props || {};
|
||||
|
||||
return importPromptWhenEmpty ? (
|
||||
<div
|
||||
className="coz-fg-hglt text-sm cursor-pointer mt-1"
|
||||
onClick={() => {
|
||||
editor?.$view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.$view.state.doc.length,
|
||||
insert: importPromptWhenEmpty,
|
||||
},
|
||||
});
|
||||
formApiRef?.current?.setValue('prompt_text', importPromptWhenEmpty);
|
||||
}}
|
||||
>
|
||||
{I18n.t('creat_new_prompt_import_link')}
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState, type ComponentProps } from 'react';
|
||||
|
||||
import { withField, FormInput, FormTextArea } from '@coze-arch/coze-design';
|
||||
|
||||
interface PromptInfoInputProps {
|
||||
readonly?: boolean;
|
||||
initCount?: number;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
rows?: number;
|
||||
field: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
maxLength?: number;
|
||||
maxCount?: number;
|
||||
rules?: ComponentProps<typeof FormInput>['rules'];
|
||||
}
|
||||
|
||||
export const PromptInfoInput = (props: PromptInfoInputProps) => {
|
||||
const { initCount, disabled, rows } = props;
|
||||
const [count, setCount] = useState(initCount || 0);
|
||||
|
||||
const handleChange = (v: string) => {
|
||||
setCount(v.length);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCount(initCount || 0);
|
||||
}, [initCount]);
|
||||
|
||||
const countSuffix = (
|
||||
<div className="overflow-hidden coz-fg-secondary text-sm pr-[9px]">{`${count}/${props.maxCount}`}</div>
|
||||
);
|
||||
|
||||
if (disabled) {
|
||||
return <ReadonlyInput {...props} />;
|
||||
}
|
||||
|
||||
if (rows && rows > 1) {
|
||||
return (
|
||||
<FormTextArea
|
||||
{...props}
|
||||
autosize
|
||||
autoComplete="off"
|
||||
onChange={(value: string) => handleChange(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormInput
|
||||
{...props}
|
||||
autoComplete="off"
|
||||
suffix={countSuffix}
|
||||
onChange={value => handleChange(value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ReadonlyInputCom = (props: PromptInfoInputProps) => {
|
||||
const { value } = props;
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="coz-fg-secondary text-base break-all whitespace-pre-line">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ReadonlyInput = withField(ReadonlyInputCom, {
|
||||
valueKey: 'value',
|
||||
onKeyChangeFnName: 'onChange',
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
import { type FormApi } from '@coze-arch/coze-design';
|
||||
|
||||
import { type PromptConfiguratorModalProps } from '../types';
|
||||
|
||||
export interface PromptConfiguratorContextType {
|
||||
props: PromptConfiguratorModalProps;
|
||||
formApiRef: React.RefObject<FormApi>;
|
||||
isReadOnly: boolean;
|
||||
}
|
||||
|
||||
export const PromptConfiguratorContext =
|
||||
createContext<PromptConfiguratorContextType | null>(null);
|
||||
|
||||
export const PromptConfiguratorProvider = PromptConfiguratorContext.Provider;
|
||||
|
||||
export const useCreatePromptContext = () =>
|
||||
useContext(PromptConfiguratorContext);
|
||||
@@ -0,0 +1,7 @@
|
||||
.prompt-configurator-modal {
|
||||
:global {
|
||||
.semi-modal-header {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { usePromptConfiguratorModal } from './use-modal';
|
||||
export type { PromptConfiguratorModalProps } from './types';
|
||||
export { ImportPromptWhenEmptyPlaceholder } from './components/placeholder/import-prompt-when-empty';
|
||||
export { useCreatePromptContext } from './context';
|
||||
export { InsertInputSlotButton } from './components/insert-input-slot';
|
||||
export { PromptConfiguratorModal } from './prompt-configurator-modal';
|
||||
export type { UsePromptConfiguratorModalProps } from './use-modal';
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
||||
/* eslint-disable max-lines-per-function */
|
||||
import { useEffect, useRef, Suspense, lazy, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useEditor,
|
||||
ActiveLinePlaceholder,
|
||||
Placeholder,
|
||||
} from '@coze-editor/editor/react';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { Modal, Form, Toast, type FormApi } from '@coze-arch/coze-design';
|
||||
import { sendTeaEvent, EVENT_NAMES } from '@coze-arch/bot-tea';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
import {
|
||||
LibraryBlockWidget,
|
||||
type ILibraryList,
|
||||
} from '@coze-common/editor-plugins/library-insert';
|
||||
import { InputSlotWidget } from '@coze-common/editor-plugins/input-slot';
|
||||
import { ActionBar } from '@coze-common/editor-plugins/action-bar';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { PromptEditorRender } from '@/editor';
|
||||
|
||||
import { type PromptConfiguratorModalProps } from './types';
|
||||
import { PromptConfiguratorProvider } from './context';
|
||||
import { PromptInfoInput } from './components/prompt-info-input';
|
||||
import { PromptHeader } from './components/header';
|
||||
import {
|
||||
CloseModal,
|
||||
PromptDiff,
|
||||
SavePrompt,
|
||||
} from './components/footer-actions';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
const MAX_NAME_LENGTH = IS_OVERSEA ? 40 : 20;
|
||||
const MAX_DESCRIPTION_LENGTH = IS_OVERSEA ? 100 : 50;
|
||||
|
||||
const NAME_ROW_LENGTH = 1;
|
||||
const DESCRIPTION_ROW_LENGTH = IS_OVERSEA ? 2 : 1;
|
||||
|
||||
interface PromptValues {
|
||||
id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
prompt_text?: string;
|
||||
}
|
||||
const EMPTY_LIBRARY: ILibraryList = [];
|
||||
|
||||
const ReactMarkdown = lazy(() => import('react-markdown'));
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
export const PromptConfiguratorModal = (
|
||||
props: PromptConfiguratorModalProps,
|
||||
) => {
|
||||
const {
|
||||
mode,
|
||||
editId,
|
||||
spaceId,
|
||||
botId,
|
||||
projectId,
|
||||
workflowId,
|
||||
canEdit,
|
||||
onUpdateSuccess,
|
||||
promptSectionConfig,
|
||||
enableDiff,
|
||||
onDiff,
|
||||
defaultPrompt,
|
||||
source,
|
||||
containerAppendSlot,
|
||||
} = props;
|
||||
const formApiRef = useRef<FormApi | null>(null);
|
||||
const editor = useEditor<EditorAPI>();
|
||||
const [modalMode, setModalMode] = useState<'info' | 'edit' | 'create'>(mode);
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const isSubmiting = useRef(false);
|
||||
const [actionBarVisible, setActionBarVisible] = useState(false);
|
||||
const selectionInInputSlotRef = useRef(false);
|
||||
const isReadOnly = modalMode === 'info';
|
||||
const {
|
||||
editorPlaceholder,
|
||||
editorActions,
|
||||
headerActions,
|
||||
editorActiveLinePlaceholder,
|
||||
editorExtensions,
|
||||
} = promptSectionConfig ?? {};
|
||||
const [formValues, setFormValues] = useState<PromptValues>({
|
||||
name: '',
|
||||
description: '',
|
||||
prompt_text: '',
|
||||
});
|
||||
const handleSubmit = async (e: React.MouseEvent<Element, MouseEvent>) => {
|
||||
if (isSubmiting.current) {
|
||||
return;
|
||||
}
|
||||
const submitValues = await formApiRef.current?.validate();
|
||||
if (!submitValues) {
|
||||
return;
|
||||
}
|
||||
isSubmiting.current = true;
|
||||
if (modalMode === 'info') {
|
||||
handleInfoModeAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (modalMode === 'create' || modalMode === 'edit') {
|
||||
const result = await handleUpdateModeAction(e);
|
||||
isSubmiting.current = false;
|
||||
sendTeaEvent(EVENT_NAMES.prompt_library_front, {
|
||||
bot_id: botId,
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
space_id: spaceId,
|
||||
prompt_id: result?.id ?? '',
|
||||
prompt_type: 'workspace',
|
||||
action: mode,
|
||||
source,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
isSubmiting.current = false;
|
||||
};
|
||||
const handleInfoModeAction = () => {
|
||||
const promptText = editor?.getValue();
|
||||
navigator.clipboard.writeText(promptText ?? '');
|
||||
Toast.success(I18n.t('prompt_library_prompt_copied_successfully'));
|
||||
};
|
||||
const handleUpdateModeAction = async (
|
||||
e: React.MouseEvent<Element, MouseEvent>,
|
||||
) => {
|
||||
try {
|
||||
const submitValues = await formApiRef.current?.validate();
|
||||
if (!submitValues) {
|
||||
return;
|
||||
}
|
||||
const res = await PlaygroundApi.UpsertPromptResource(
|
||||
{
|
||||
prompt: {
|
||||
...submitValues,
|
||||
space_id: spaceId,
|
||||
...(modalMode === 'edit' && { id: editId }),
|
||||
},
|
||||
},
|
||||
{
|
||||
__disableErrorToast: true,
|
||||
},
|
||||
);
|
||||
props.onCancel?.(e);
|
||||
const id = modalMode === 'edit' ? editId : res?.data?.id;
|
||||
if (mode === 'create') {
|
||||
Toast.success(I18n.t('prompt_library_prompt_creat_successfully'));
|
||||
}
|
||||
onUpdateSuccess?.(mode, id);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
mode,
|
||||
id,
|
||||
};
|
||||
} catch (error) {
|
||||
setErrMsg((error as Error).message);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!defaultPrompt || !editor) {
|
||||
return;
|
||||
}
|
||||
editor?.$view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.$view.state.doc.length,
|
||||
insert: defaultPrompt,
|
||||
},
|
||||
});
|
||||
}, [defaultPrompt, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editId || !editor) {
|
||||
return;
|
||||
}
|
||||
PlaygroundApi.GetPromptResourceInfo({
|
||||
prompt_resource_id: editId,
|
||||
}).then(
|
||||
({ data: { name = '', description = '', prompt_text = '' } = {} }) => {
|
||||
formApiRef.current?.setValues({
|
||||
prompt_text,
|
||||
name,
|
||||
description,
|
||||
});
|
||||
editor?.$view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.$view.state.doc.length,
|
||||
insert: prompt_text,
|
||||
},
|
||||
});
|
||||
setFormValues({
|
||||
name,
|
||||
description,
|
||||
prompt_text,
|
||||
});
|
||||
},
|
||||
);
|
||||
}, [editId, modalMode, editor]);
|
||||
|
||||
return (
|
||||
<PromptConfiguratorProvider
|
||||
value={{
|
||||
props,
|
||||
formApiRef,
|
||||
isReadOnly,
|
||||
}}
|
||||
>
|
||||
<Modal
|
||||
title={
|
||||
<PromptHeader
|
||||
canEdit={!!canEdit}
|
||||
mode={modalMode}
|
||||
onEditIconClick={() => {
|
||||
setModalMode('edit');
|
||||
}}
|
||||
/>
|
||||
}
|
||||
closeOnEsc={false}
|
||||
maskClosable={false}
|
||||
visible
|
||||
width="640px"
|
||||
footer={
|
||||
<div className="flex items-center justify-end">
|
||||
{enableDiff ? (
|
||||
<PromptDiff
|
||||
spaceId={spaceId}
|
||||
botId={botId}
|
||||
projectId={projectId}
|
||||
workflowId={workflowId}
|
||||
source={source}
|
||||
mode={modalMode}
|
||||
editor={editor}
|
||||
submitFun={handleSubmit}
|
||||
editId={editId}
|
||||
onDiff={({ prompt, libraryId }) => {
|
||||
onDiff?.({ prompt, libraryId });
|
||||
}}
|
||||
onCancel={e => {
|
||||
props.onCancel?.(e);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CloseModal onCancel={props.onCancel} />
|
||||
)}
|
||||
<SavePrompt
|
||||
mode={modalMode}
|
||||
isSubmitting={isSubmiting.current}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
onCancel={props.onCancel}
|
||||
className={styles['prompt-configurator-modal']}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<Form<PromptValues>
|
||||
getFormApi={formApi => {
|
||||
formApiRef.current = formApi;
|
||||
}}
|
||||
>
|
||||
<PromptInfoInput
|
||||
disabled={modalMode === 'info'}
|
||||
label={I18n.t('creat_new_prompt_prompt_name')}
|
||||
placeholder={I18n.t('creat_new_prompt_name_placeholder')}
|
||||
maxLength={MAX_NAME_LENGTH}
|
||||
maxCount={MAX_NAME_LENGTH}
|
||||
initCount={formValues.name.length}
|
||||
rows={NAME_ROW_LENGTH}
|
||||
rules={[
|
||||
{
|
||||
required: !isReadOnly,
|
||||
message: I18n.t('creat_new_prompt_name_placeholder'),
|
||||
},
|
||||
]}
|
||||
field="name"
|
||||
/>
|
||||
<PromptInfoInput
|
||||
disabled={modalMode === 'info'}
|
||||
label={I18n.t('creat_new_prompt_prompt_description')}
|
||||
placeholder={I18n.t('creat_new_prompt_des_placeholder')}
|
||||
maxLength={MAX_DESCRIPTION_LENGTH}
|
||||
maxCount={MAX_DESCRIPTION_LENGTH}
|
||||
initCount={formValues.description.length}
|
||||
rows={DESCRIPTION_ROW_LENGTH}
|
||||
field="description"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<Form.Label
|
||||
text={I18n.t('creat_new_prompt_prompt')}
|
||||
className="mb-0"
|
||||
/>
|
||||
{headerActions}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-lg border border-solid coz-stroke-plus h-[400px] overflow-y-auto styled-scrollbar hover-show-scrollbar',
|
||||
)}
|
||||
>
|
||||
<PromptEditorRender
|
||||
readonly={modalMode === 'info'}
|
||||
options={{
|
||||
minHeight: 300,
|
||||
}}
|
||||
onChange={value => {
|
||||
formApiRef.current?.setValue('prompt_text', value);
|
||||
}}
|
||||
/>
|
||||
<InputSlotWidget
|
||||
mode="configurable"
|
||||
onSelectionInInputSlot={selection => {
|
||||
selectionInInputSlotRef.current = !!selection;
|
||||
}}
|
||||
/>
|
||||
<LibraryBlockWidget
|
||||
librarys={EMPTY_LIBRARY}
|
||||
readonly
|
||||
spaceId={spaceId}
|
||||
/>
|
||||
<ActionBar
|
||||
trigger="custom"
|
||||
visible={actionBarVisible}
|
||||
onVisibleChange={visible => {
|
||||
if (selectionInInputSlotRef.current) {
|
||||
return;
|
||||
}
|
||||
setActionBarVisible(visible);
|
||||
}}
|
||||
>
|
||||
{editorActions}
|
||||
</ActionBar>
|
||||
<Placeholder>{editorPlaceholder}</Placeholder>
|
||||
<ActiveLinePlaceholder>
|
||||
{editorActiveLinePlaceholder}
|
||||
</ActiveLinePlaceholder>
|
||||
{editorExtensions}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
{errMsg ? (
|
||||
<div className="text-red">
|
||||
<Suspense fallback={null}>
|
||||
<ReactMarkdown skipHtml={true} linkTarget="_blank">
|
||||
{errMsg}
|
||||
</ReactMarkdown>
|
||||
</Suspense>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
{containerAppendSlot}
|
||||
</PromptConfiguratorProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ModalProps } from '@coze-arch/coze-design';
|
||||
|
||||
export interface PromptContextInfo {
|
||||
botId?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
contextHistory?: string;
|
||||
}
|
||||
|
||||
export interface PromptConfiguratorModalProps extends ModalProps {
|
||||
mode: 'create' | 'edit' | 'info';
|
||||
editId?: string;
|
||||
isPersonal?: boolean;
|
||||
spaceId: string;
|
||||
botId?: string;
|
||||
projectId?: string;
|
||||
workflowId?: string;
|
||||
defaultPrompt?: string;
|
||||
canEdit?: boolean;
|
||||
/** 用于埋点: 页面来源 */
|
||||
source: string;
|
||||
enableDiff?: boolean;
|
||||
promptSectionConfig?: {
|
||||
/** 提示词输入框的 placeholder */
|
||||
editorPlaceholder?: React.ReactNode;
|
||||
/** 提示词划词actions */
|
||||
editorActions?: React.ReactNode;
|
||||
/** 头部 actions */
|
||||
headerActions?: React.ReactNode;
|
||||
/** 提示词输入框的 active line placeholder */
|
||||
editorActiveLinePlaceholder?: React.ReactNode;
|
||||
/** 提示词输入框的 extensions */
|
||||
editorExtensions?: React.ReactNode;
|
||||
};
|
||||
/** 最外层容器插槽 */
|
||||
containerAppendSlot?: React.ReactNode;
|
||||
importPromptWhenEmpty?: string;
|
||||
getConversationId?: () => string | undefined;
|
||||
getPromptContextInfo?: () => PromptContextInfo;
|
||||
onUpdateSuccess?: (mode: 'create' | 'edit' | 'info', id?: string) => void;
|
||||
onDiff?: ({
|
||||
prompt,
|
||||
libraryId,
|
||||
}: {
|
||||
prompt: string;
|
||||
libraryId: string;
|
||||
}) => void;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { useState } from 'react';
|
||||
|
||||
import { PromptEditorProvider } from '@/editor';
|
||||
|
||||
import { type PromptConfiguratorModalProps } from './types';
|
||||
import { PromptConfiguratorModal } from './prompt-configurator-modal';
|
||||
type DynamicProps = Pick<
|
||||
PromptConfiguratorModalProps,
|
||||
'mode' | 'editId' | 'canEdit' | 'defaultPrompt'
|
||||
>;
|
||||
|
||||
export type UsePromptConfiguratorModalProps = Pick<
|
||||
PromptConfiguratorModalProps,
|
||||
| 'spaceId'
|
||||
| 'getConversationId'
|
||||
| 'getPromptContextInfo'
|
||||
| 'onUpdateSuccess'
|
||||
| 'importPromptWhenEmpty'
|
||||
| 'onDiff'
|
||||
| 'enableDiff'
|
||||
| 'isPersonal'
|
||||
| 'source'
|
||||
| 'botId'
|
||||
| 'projectId'
|
||||
| 'workflowId'
|
||||
> &
|
||||
Partial<DynamicProps> & {
|
||||
CustomPromptConfiguratorModal?: (
|
||||
props: PromptConfiguratorModalProps,
|
||||
) => React.JSX.Element;
|
||||
};
|
||||
export const usePromptConfiguratorModal = (
|
||||
props: UsePromptConfiguratorModalProps,
|
||||
) => {
|
||||
const { CustomPromptConfiguratorModal = PromptConfiguratorModal } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [dynamicProps, setDynamicProps] = useState<DynamicProps>({
|
||||
mode: 'create',
|
||||
editId: '',
|
||||
canEdit: true,
|
||||
defaultPrompt: '',
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
const open = (
|
||||
options: Pick<
|
||||
PromptConfiguratorModalProps,
|
||||
'mode' | 'editId' | 'canEdit' | 'defaultPrompt'
|
||||
>,
|
||||
) => {
|
||||
setVisible(true);
|
||||
setDynamicProps(options);
|
||||
};
|
||||
return {
|
||||
node: visible ? (
|
||||
<PromptEditorProvider>
|
||||
<CustomPromptConfiguratorModal
|
||||
{...props}
|
||||
{...dynamicProps}
|
||||
onCancel={close}
|
||||
/>
|
||||
</PromptEditorProvider>
|
||||
) : null,
|
||||
close,
|
||||
open,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
import { EditorProvider } from '@coze-editor/editor/react';
|
||||
|
||||
export const PromptEditorProvider: React.FC<PropsWithChildren> = ({
|
||||
children,
|
||||
}) => <EditorProvider>{children}</EditorProvider>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { PromptEditorRender } from './render';
|
||||
export { PromptEditorProvider } from './context';
|
||||
export { useEditor, ActiveLinePlaceholder } from '@coze-editor/editor/react';
|
||||
|
||||
export type { EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
export type { PromptEditorRenderProps } from './render';
|
||||
158
frontend/packages/common/prompt-kit/base/src/editor/render.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useCallback, useRef, useEffect, type ReactNode, useMemo } from 'react';
|
||||
|
||||
import { merge } from 'lodash-es';
|
||||
import { Renderer, Placeholder, useEditor } from '@coze-editor/editor/react';
|
||||
// promptPreset是针对Prompt提供了一系列内置扩展的集合
|
||||
import promptPreset, {
|
||||
type EditorAPI,
|
||||
} from '@coze-editor/editor/preset-prompt';
|
||||
import { ThemeExtension } from '@coze-common/editor-plugins/theme';
|
||||
import { SyntaxHighlight } from '@coze-common/editor-plugins/syntax-highlight';
|
||||
import { LanguageSupport } from '@coze-common/editor-plugins/language-support';
|
||||
|
||||
import { defaultTheme } from '@/theme/default';
|
||||
|
||||
export interface PromptEditorRenderProps {
|
||||
readonly?: boolean; // 所有插入编辑器相关操作禁用:action-bar
|
||||
placeholder?: ReactNode;
|
||||
className?: string;
|
||||
dataTestID?: string;
|
||||
defaultValue?: string;
|
||||
fontSize?: number;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
onFocus?: () => void;
|
||||
/**
|
||||
* 光标焦点丢失
|
||||
*/
|
||||
onBlur?: () => void;
|
||||
options?: Record<string, string | number>;
|
||||
isControled?: boolean; // 是否受控
|
||||
getEditor?: (editor: EditorAPI) => void;
|
||||
}
|
||||
|
||||
export const PromptEditorRender: React.FC<PromptEditorRenderProps> = props => {
|
||||
const {
|
||||
readonly,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
className,
|
||||
dataTestID,
|
||||
value,
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
options,
|
||||
isControled,
|
||||
getEditor,
|
||||
} = props;
|
||||
const apiRef = useRef<EditorAPI | null>(null);
|
||||
const editor = useEditor<EditorAPI>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !onBlur) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.$on('blur', onBlur);
|
||||
|
||||
return () => {
|
||||
editor.$off('blur', onBlur);
|
||||
};
|
||||
}, [editor, onBlur]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !onFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.$on('focus', onFocus);
|
||||
|
||||
return () => {
|
||||
editor.$off('focus', onFocus);
|
||||
};
|
||||
}, [editor, onFocus]);
|
||||
|
||||
// 值受控
|
||||
useEffect(() => {
|
||||
const curEditor = apiRef.current;
|
||||
|
||||
if (!curEditor || !isControled || !editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const preVal = curEditor.getValue();
|
||||
if (typeof value === 'string' && value !== preVal) {
|
||||
editor.$view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.$view.state.doc.length,
|
||||
insert: value ?? '',
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [isControled, value, editor]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: { value: string }) => {
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(e.value);
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const contentAttributes = useMemo(
|
||||
() => ({
|
||||
class: className ?? '',
|
||||
'data-testid': dataTestID ?? '',
|
||||
}),
|
||||
[className, dataTestID],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Renderer
|
||||
plugins={promptPreset}
|
||||
defaultValue={defaultValue}
|
||||
options={merge(
|
||||
{
|
||||
fontSize: 14,
|
||||
contentAttributes,
|
||||
editable: !readonly,
|
||||
readOnly: readonly,
|
||||
},
|
||||
options,
|
||||
)}
|
||||
onChange={handleChange}
|
||||
didMount={api => {
|
||||
apiRef.current = api;
|
||||
if (getEditor) {
|
||||
getEditor(api);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Placeholder>{placeholder}</Placeholder>
|
||||
<ThemeExtension themes={[defaultTheme]} />
|
||||
<LanguageSupport />
|
||||
<SyntaxHighlight.Markdown />
|
||||
<SyntaxHighlight.Jinja />
|
||||
</>
|
||||
);
|
||||
};
|
||||
19
frontend/packages/common/prompt-kit/base/src/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { PromptEditorRender } from './editor/render';
|
||||
export { PromptEditorProvider } from './editor/context';
|
||||
export type { PromptEditorRenderProps } from './editor/render';
|
||||
@@ -0,0 +1,11 @@
|
||||
.hover-show-scrollbar {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(6, 7, 9, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useEditor } from '@coze-editor/editor/react';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { type ViewUpdate } from '@codemirror/view';
|
||||
export const useReadonly = () => {
|
||||
const editor = useEditor<EditorAPI>();
|
||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||
useEffect(() => {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
setIsReadOnly(editor.$view.state.readOnly);
|
||||
const handleViewUpdate = (update: ViewUpdate) => {
|
||||
if (update.startState.readOnly !== update.state.readOnly) {
|
||||
setIsReadOnly(update.state.readOnly);
|
||||
}
|
||||
};
|
||||
editor.$on('viewUpdate', handleViewUpdate);
|
||||
return () => {
|
||||
editor.$off('viewUpdate', handleViewUpdate);
|
||||
};
|
||||
}, [editor]);
|
||||
return isReadOnly;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { localStorageService } from '@coze-foundation/local-storage';
|
||||
|
||||
const SESSION_HIDDEN_KEY = 'coze-promptkit-recommend-pannel-hidden-key';
|
||||
|
||||
export const useSetSessionVisiblePersist = (key: string) => {
|
||||
const [isSessionVisible, setIsSessionVisible] = useState(isKeyExist(key));
|
||||
return {
|
||||
isSessionVisible,
|
||||
toggleSessionVisible: (visible: boolean) => {
|
||||
const oldValue = localStorageService.getValue(SESSION_HIDDEN_KEY) || '';
|
||||
if (isKeyExist(key) && visible) {
|
||||
return;
|
||||
}
|
||||
if (visible) {
|
||||
localStorageService.setValue(
|
||||
SESSION_HIDDEN_KEY,
|
||||
oldValue ? `${oldValue},${key}` : key,
|
||||
);
|
||||
setIsSessionVisible(true);
|
||||
return;
|
||||
}
|
||||
localStorageService.setValue(
|
||||
SESSION_HIDDEN_KEY,
|
||||
oldValue.replace(key, ''),
|
||||
);
|
||||
setIsSessionVisible(false);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const isKeyExist = (key: string) => {
|
||||
const oldValue = localStorageService.getValue(SESSION_HIDDEN_KEY);
|
||||
return oldValue?.includes(key);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
createFreeGrabModalHierarchyStore,
|
||||
type FreeGrabModalHierarchyStore,
|
||||
} from './service/free-grab-modal-hierarchy-service/store';
|
||||
export { FreeGrabModalHierarchyService } from './service/free-grab-modal-hierarchy-service';
|
||||
export { getSelectionBoundary } from './utils/rect';
|
||||
export { useReadonly } from './hooks/use-editor-readonly';
|
||||
export { insertToNewline } from './utils/insert-to-newline';
|
||||
export { type PromptContextInfo } from './types';
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ModalHierarchyServiceConstructor } from './type';
|
||||
import { type FreeGrabModalHierarchyAction } from './store';
|
||||
|
||||
export class FreeGrabModalHierarchyService {
|
||||
/** Tip: semi modal zIndex 为 1000 */
|
||||
private baseZIndex = 1000;
|
||||
public registerModal: FreeGrabModalHierarchyAction['registerModal'];
|
||||
public removeModal: FreeGrabModalHierarchyAction['removeModal'];
|
||||
public onFocus: FreeGrabModalHierarchyAction['setModalToTopLayer'];
|
||||
private getModalIndex: FreeGrabModalHierarchyAction['getModalIndex'];
|
||||
|
||||
constructor({
|
||||
registerModal,
|
||||
removeModal,
|
||||
getModalIndex,
|
||||
setModalToTopLayer,
|
||||
}: ModalHierarchyServiceConstructor) {
|
||||
this.registerModal = registerModal;
|
||||
this.removeModal = removeModal;
|
||||
this.getModalIndex = getModalIndex;
|
||||
this.onFocus = setModalToTopLayer;
|
||||
}
|
||||
|
||||
public getModalZIndex = (keyOrIndex: string | number) => {
|
||||
if (typeof keyOrIndex === 'string') {
|
||||
return this.getModalIndex(keyOrIndex) + this.baseZIndex;
|
||||
}
|
||||
return keyOrIndex + this.baseZIndex;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import { produce } from 'immer';
|
||||
|
||||
export interface FreeGrabModalHierarchyState {
|
||||
// modal 的 key list
|
||||
modalHierarchyList: string[];
|
||||
}
|
||||
|
||||
export interface FreeGrabModalHierarchyAction {
|
||||
registerModal: (key: string) => void;
|
||||
removeModal: (key: string) => void;
|
||||
getModalIndex: (key: string) => number;
|
||||
setModalToTopLayer: (key: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可自由拖拽的弹窗之间的层级关系
|
||||
*/
|
||||
export const createFreeGrabModalHierarchyStore = () =>
|
||||
create<FreeGrabModalHierarchyState & FreeGrabModalHierarchyAction>()(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
modalHierarchyList: [],
|
||||
getModalIndex: key =>
|
||||
get().modalHierarchyList.findIndex(modalKey => modalKey === key),
|
||||
registerModal: key => {
|
||||
set(
|
||||
{
|
||||
modalHierarchyList: produce(get().modalHierarchyList, draft => {
|
||||
draft.unshift(key);
|
||||
}),
|
||||
},
|
||||
false,
|
||||
'registerModal',
|
||||
);
|
||||
},
|
||||
removeModal: key => {
|
||||
set(
|
||||
{
|
||||
modalHierarchyList: produce(get().modalHierarchyList, draft => {
|
||||
const index = get().getModalIndex(key);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
draft.splice(index, 1);
|
||||
}),
|
||||
},
|
||||
false,
|
||||
'removeModal',
|
||||
);
|
||||
},
|
||||
|
||||
setModalToTopLayer: key => {
|
||||
set(
|
||||
{
|
||||
modalHierarchyList: produce(get().modalHierarchyList, draft => {
|
||||
const index = get().getModalIndex(key);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
get().removeModal(key);
|
||||
get().registerModal(key);
|
||||
}),
|
||||
},
|
||||
false,
|
||||
'setModalToTopLayer',
|
||||
);
|
||||
},
|
||||
}),
|
||||
{
|
||||
enabled: IS_DEV_MODE,
|
||||
name: 'botStudio.botEditor.ModalHierarchy',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
export type FreeGrabModalHierarchyStore = ReturnType<
|
||||
typeof createFreeGrabModalHierarchyStore
|
||||
>;
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FreeGrabModalHierarchyAction } from './store';
|
||||
|
||||
export interface ModalHierarchyServiceConstructor {
|
||||
registerModal: FreeGrabModalHierarchyAction['registerModal'];
|
||||
removeModal: FreeGrabModalHierarchyAction['removeModal'];
|
||||
setModalToTopLayer: FreeGrabModalHierarchyAction['setModalToTopLayer'];
|
||||
getModalIndex: FreeGrabModalHierarchyAction['getModalIndex'];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { type PromptContextInfo } from './prompt';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface PromptContextInfo {
|
||||
botId?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
contextHistory?: string;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
|
||||
export const insertToNewline = async ({
|
||||
editor,
|
||||
prompt,
|
||||
}: {
|
||||
editor?: EditorAPI;
|
||||
prompt: string;
|
||||
}): Promise<string> => {
|
||||
if (!editor) {
|
||||
return '';
|
||||
}
|
||||
const { state } = editor.$view;
|
||||
const isDocEmpty = state.doc.length === 0;
|
||||
const insertPrompt = isDocEmpty ? prompt : `\n${prompt}`;
|
||||
const selection = isDocEmpty
|
||||
? undefined
|
||||
: {
|
||||
anchor: state.doc.length,
|
||||
head: state.doc.length + insertPrompt.length,
|
||||
};
|
||||
|
||||
editor.$view.dispatch({
|
||||
changes: {
|
||||
from: state.doc.length,
|
||||
to: state.doc.length,
|
||||
insert: insertPrompt,
|
||||
},
|
||||
selection,
|
||||
scrollIntoView: true,
|
||||
});
|
||||
// 等待下一个微任务周期,确保状态已更新
|
||||
await Promise.resolve();
|
||||
|
||||
// 使用更新后的state获取最新文档内容
|
||||
const newDoc = editor.$view.state.doc.toString();
|
||||
|
||||
// 插入到新一行
|
||||
// 注意:该操作提前会触发 chrome bug 导致崩溃问题
|
||||
editor.focus();
|
||||
return newDoc;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
|
||||
export const getSelectionBoundary = (editor: EditorAPI) => {
|
||||
const rects = editor.getMainSelectionRects();
|
||||
|
||||
if (rects.length === 0) {
|
||||
return { left: 0, top: 0, width: 0, height: 0 };
|
||||
}
|
||||
|
||||
// 初始化最大矩形
|
||||
let maxLeft = Infinity;
|
||||
let maxTop = Infinity;
|
||||
let maxRight = -Infinity;
|
||||
let maxBottom = -Infinity;
|
||||
|
||||
// 遍历所有矩形,计算包围盒的边界
|
||||
rects.forEach(rect => {
|
||||
maxLeft = Math.min(maxLeft, rect.left);
|
||||
maxTop = Math.min(maxTop, rect.top);
|
||||
maxRight = Math.max(maxRight, rect.left + (rect.width ?? 0));
|
||||
maxBottom = Math.max(maxBottom, rect.top + (rect.height ?? 0));
|
||||
});
|
||||
|
||||
// 计算最终的宽度和高度
|
||||
const width = maxRight - maxLeft;
|
||||
const height = maxBottom - maxTop;
|
||||
|
||||
// 获取编辑器的滚动位置
|
||||
const { scrollLeft } = editor.$view.scrollDOM;
|
||||
const { scrollTop } = editor.$view.scrollDOM;
|
||||
|
||||
// 获取编辑器容器的位置
|
||||
const editorRect = editor.$view.dom.getBoundingClientRect();
|
||||
|
||||
// 计算相对于视口的绝对位置
|
||||
const absoluteLeft = editorRect.left + maxLeft - scrollLeft;
|
||||
const absoluteTop = editorRect.top + maxTop - scrollTop;
|
||||
const absoluteBottom = editorRect.top + maxBottom - scrollTop;
|
||||
|
||||
return {
|
||||
left: absoluteLeft,
|
||||
top: absoluteTop,
|
||||
bottom: absoluteBottom,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type Extension } from '@coze-common/editor-plugins/types';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
|
||||
export const defaultTheme: Extension = EditorView.theme({
|
||||
'.cm-content': {
|
||||
color: 'rgba(6, 7, 9, 0.80)',
|
||||
},
|
||||
'.cm-line': {
|
||||
lineHeight: '24px',
|
||||
paddingLeft: '12px',
|
||||
},
|
||||
'.cm-cursor': {
|
||||
height: '20px !important',
|
||||
},
|
||||
});
|
||||
23
frontend/packages/common/prompt-kit/base/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
|
||||
declare const IS_OVERSEA: boolean;
|
||||
declare const IS_CN_REGION: boolean;
|
||||
declare const IS_DEV_MODE: boolean;
|
||||
declare const IS_PROD_MODE: boolean;
|
||||
declare const IS_RELEASE_VERSION: boolean;
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DemoComponent } from '../src';
|
||||
|
||||
export default {
|
||||
title: 'Example/Demo',
|
||||
component: DemoComponent,
|
||||
parameters: {
|
||||
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
|
||||
layout: 'centered',
|
||||
},
|
||||
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
|
||||
tags: ['autodocs'],
|
||||
// More on argTypes: https://storybook.js.org/docs/api/argtypes
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
||||
export const Base = {
|
||||
args: {
|
||||
name: 'tecvan',
|
||||
},
|
||||
};
|
||||
34
frontend/packages/common/prompt-kit/base/stories/hello.mdx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Meta } from "@storybook/blocks";
|
||||
|
||||
<Meta title="Hello world" />
|
||||
|
||||
<div className="sb-container">
|
||||
<div className='sb-section-title'>
|
||||
# Hello world
|
||||
|
||||
Hello world
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
{`
|
||||
.sb-container {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.sb-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.sb-section-title {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
50
frontend/packages/common/prompt-kit/base/tsconfig.build.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"types": [],
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "bundler",
|
||||
"module": "ESNext",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../arch/bot-api/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../arch/bot-tea/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../arch/i18n/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/eslint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/stylelint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/ts-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/vitest-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../editor-plugins/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../foundation/local-storage/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/common/prompt-kit/base/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"exclude": ["**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
frontend/packages/common/prompt-kit/base/tsconfig.misc.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"include": ["__tests__", "stories", "vitest.config.ts", "tailwind.config.ts"],
|
||||
"exclude": ["./dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"types": ["vitest/globals"],
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "bundler",
|
||||
"module": "ESNext"
|
||||
}
|
||||
}
|
||||
22
frontend/packages/common/prompt-kit/base/vitest.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineConfig } from '@coze-arch/vitest-config';
|
||||
|
||||
export default defineConfig({
|
||||
dirname: __dirname,
|
||||
preset: 'web',
|
||||
});
|
||||
31
frontend/packages/common/prompt-kit/main/.storybook/main.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
|
||||
/** @type { import('@storybook/react-vite').StorybookConfig } */
|
||||
const config = {
|
||||
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.tsx'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-onboarding',
|
||||
'@storybook/addon-interactions',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
viteFinal: config =>
|
||||
mergeConfig(config, {
|
||||
plugins: [
|
||||
svgr({
|
||||
svgrOptions: {
|
||||
native: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,14 @@
|
||||
/** @type { import('@storybook/react').Preview } */
|
||||
const preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
5
frontend/packages/common/prompt-kit/main/.stylelintrc.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
});
|
||||
16
frontend/packages/common/prompt-kit/main/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# @coze-common/prompt-kit
|
||||
|
||||
> Project template for react component with storybook.
|
||||
|
||||
## Features
|
||||
|
||||
- [x] eslint & ts
|
||||
- [x] esm bundle
|
||||
- [x] umd bundle
|
||||
- [x] storybook
|
||||
|
||||
## Commands
|
||||
|
||||
- init: `rush update`
|
||||
- dev: `npm run dev`
|
||||
- build: `npm run build`
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {},
|
||||
});
|
||||
101
frontend/packages/common/prompt-kit/main/package.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "@coze-common/prompt-kit",
|
||||
"version": "0.0.1",
|
||||
"description": "prompt编辑器,基于@coze-editor/editor",
|
||||
"license": "Apache-2.0",
|
||||
"author": "haozhenfei@bytedance.com",
|
||||
"maintainers": [],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/",
|
||||
"./editor": "./src/editor/index.ts",
|
||||
"./prompt-recommend": "./src/prompt-recommend/index.ts",
|
||||
"./create-prompt": "./src/create-prompt/index.ts",
|
||||
"./nl-prompt": "./src/nl-prompt/index.ts"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"editor": [
|
||||
"./src/editor/index.ts"
|
||||
],
|
||||
"prompt-recommend": [
|
||||
"./src/prompt-recommend/index.ts"
|
||||
],
|
||||
"create-prompt": [
|
||||
"./src/create-prompt/index.ts"
|
||||
],
|
||||
"nl-prompt": [
|
||||
"./src/nl-prompt/index.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "npm run test -- --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.34.1",
|
||||
"@coze-agent-ide/bot-editor-context-store": "workspace:*",
|
||||
"@coze-arch/bot-api": "workspace:*",
|
||||
"@coze-arch/bot-error": "workspace:*",
|
||||
"@coze-arch/bot-http": "workspace:*",
|
||||
"@coze-arch/bot-icons": "workspace:*",
|
||||
"@coze-arch/bot-semi": "workspace:*",
|
||||
"@coze-arch/bot-tea": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-arch/idl": "workspace:*",
|
||||
"@coze-common/chat-answer-action": "workspace:*",
|
||||
"@coze-common/chat-area-utils": "workspace:*",
|
||||
"@coze-common/chat-core": "workspace:*",
|
||||
"@coze-common/chat-uikit": "workspace:*",
|
||||
"@coze-common/editor-plugins": "workspace:*",
|
||||
"@coze-common/prompt-kit-adapter": "workspace:*",
|
||||
"@coze-common/prompt-kit-base": "workspace:*",
|
||||
"@coze-common/scroll-view": "workspace:*",
|
||||
"@coze-editor/editor": "0.1.0-alpha.d92d50",
|
||||
"@coze-foundation/local-storage": "workspace:*",
|
||||
"@lezer/common": "^1.2.2",
|
||||
"ahooks": "^3.7.8",
|
||||
"classnames": "^2.3.2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"immer": "^10.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^4.0.2",
|
||||
"rc-textarea": "^1.6.3",
|
||||
"react-markdown": "^8.0.3",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@coze-arch/bot-env": "workspace:*",
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-svgr": "~3.3.0",
|
||||
"vitest": "~3.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.2.0",
|
||||
"react-dom": ">=18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,145 @@
|
||||
<svg width="356" height="178" viewBox="0 0 356 178" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.2">
|
||||
<mask id="mask0_7107_191040" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="356" height="178">
|
||||
<rect x="0.540771" width="354.918" height="177.459" fill="url(#paint0_linear_7107_191040)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_7107_191040)">
|
||||
<mask id="mask1_7107_191040" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="356" height="178">
|
||||
<rect x="0.540771" width="354.918" height="177.459" fill="url(#paint1_linear_7107_191040)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_7107_191040)">
|
||||
<rect x="0.540771" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" width="29.5765" height="29.5765" fill="url(#paint2_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="29.5767" width="29.5765" height="29.5765" fill="url(#paint3_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="29.5767" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="59.1528" width="29.5765" height="29.5765" fill="url(#paint4_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="59.1528" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="88.7295" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="118.306" width="29.5765" height="29.5765" fill="url(#paint5_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="118.306" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="0.540771" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="30.1172" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="59.6938" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="89.2705" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="118.847" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="148.424" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="178" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="207.576" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="237.153" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="266.729" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="296.306" y="147.883" width="29.5765" height="29.5765" fill="url(#paint6_linear_7107_191040)" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
<rect x="325.883" y="147.883" width="29.5765" height="29.5765" stroke="#3C445C" stroke-width="0.616178"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<rect x="80.7778" y="82.3135" width="194.444" height="70.8333" rx="5.55556" fill="#F9F9F9"/>
|
||||
<rect x="81.1251" y="82.6607" width="193.75" height="70.1389" rx="5.20833" stroke="url(#paint7_linear_7107_191040)" stroke-opacity="0.24" stroke-width="0.694444"/>
|
||||
<rect opacity="0.12" x="91.8889" y="94.814" width="72.2222" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<rect opacity="0.12" x="91.8889" y="110.092" width="136.111" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<g filter="url(#filter0_d_7107_191040)">
|
||||
<path d="M236.333 97.8693C236.333 96.3135 236.333 95.5357 236.636 94.9415C236.902 94.4188 237.327 93.9939 237.85 93.7276C238.444 93.4248 239.222 93.4248 240.778 93.4248H259.667C261.222 93.4248 262 93.4248 262.594 93.7276C263.117 93.9939 263.542 94.4188 263.808 94.9415C264.111 95.5357 264.111 96.3135 264.111 97.8692V116.758C264.111 118.314 264.111 119.092 263.808 119.686C263.542 120.209 263.117 120.634 262.594 120.9C262 121.203 261.222 121.203 259.667 121.203H240.778C239.222 121.203 238.444 121.203 237.85 120.9C237.327 120.634 236.902 120.209 236.636 119.686C236.333 119.092 236.333 118.314 236.333 116.758V97.8693Z" fill="url(#paint8_linear_7107_191040)"/>
|
||||
<path d="M236.403 97.8693C236.403 97.0903 236.403 96.5102 236.44 96.0505C236.478 95.5916 236.552 95.2587 236.698 94.973C236.958 94.4634 237.372 94.0491 237.881 93.7894C238.167 93.6439 238.5 93.5694 238.959 93.5319C239.419 93.4943 239.999 93.4942 240.778 93.4942H259.667C260.446 93.4942 261.026 93.4943 261.485 93.5319C261.944 93.5694 262.277 93.6439 262.563 93.7894C263.072 94.0491 263.487 94.4634 263.746 94.973C263.892 95.2587 263.966 95.5916 264.004 96.0505C264.042 96.5102 264.042 97.0903 264.042 97.8692V116.758C264.042 117.537 264.042 118.117 264.004 118.577C263.966 119.036 263.892 119.369 263.746 119.654C263.487 120.164 263.072 120.578 262.563 120.838C262.277 120.984 261.944 121.058 261.485 121.096C261.026 121.133 260.446 121.133 259.667 121.133H240.778C239.999 121.133 239.419 121.133 238.959 121.096C238.5 121.058 238.167 120.984 237.881 120.838C237.372 120.578 236.958 120.164 236.698 119.654C236.552 119.369 236.478 119.036 236.44 118.577C236.403 118.117 236.403 117.537 236.403 116.758V97.8693Z" stroke="black" stroke-opacity="0.08" stroke-width="0.138889"/>
|
||||
</g>
|
||||
<rect opacity="0.12" x="91.8889" y="132.313" width="172.222" height="9.72222" rx="4.86111" fill="#2B3245"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_7107_191040" x="230.381" y="89.4566" width="39.6826" height="39.6826" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1.98413"/>
|
||||
<feGaussianBlur stdDeviation="2.97619"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_7107_191040"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_7107_191040" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_7107_191040" x1="355.459" y1="88.7296" x2="0.540769" y2="88.7296" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
<stop offset="0.500048" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_7107_191040" x1="178" y1="0" x2="178" y2="177.459" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
<stop offset="0.493585" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_7107_191040" x1="44.9055" y1="0" x2="44.9055" y2="29.5765" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_7107_191040" x1="133.635" y1="29.5767" x2="133.635" y2="59.1532" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_7107_191040" x1="222.365" y1="59.1528" x2="222.365" y2="88.7294" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_7107_191040" x1="163.212" y1="118.306" x2="163.212" y2="147.883" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_7107_191040" x1="311.094" y1="147.883" x2="311.094" y2="177.459" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3C445C" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#3C445C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_7107_191040" x1="178" y1="82.3135" x2="178" y2="153.147" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#38415A"/>
|
||||
<stop offset="1" stop-color="#2B3245" stop-opacity="0.4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_7107_191040" x1="250.222" y1="93.4248" x2="250.222" y2="121.203" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F45D68"/>
|
||||
<stop offset="1" stop-color="#FFCA00"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 43 KiB |
19
frontend/packages/common/prompt-kit/main/src/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import '@coze-arch/bot-typings';
|
||||
import '@coze-arch/bot-env/typings';
|
||||
export { usePromptLibraryModal } from './prompt-library';
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozEmpty } from '@coze-arch/coze-design/icons';
|
||||
import { EmptyState } from '@coze-arch/coze-design';
|
||||
|
||||
import EmptyPromptIcon from '../assets/empty-prompt-icon.svg';
|
||||
|
||||
export const UnselectedPrompt = (props: { className?: string }) => (
|
||||
<div className={props.className}>
|
||||
<EmptyState
|
||||
title={I18n.t('prompt_library_unselected')}
|
||||
icon={<img src={EmptyPromptIcon} alt="empty-prompt" />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const EmptyPrompt = (props: { className?: string }) => (
|
||||
<div className={props.className}>
|
||||
<EmptyState
|
||||
title={I18n.t('prompt_library_prompt_empty')}
|
||||
icon={<IconCozEmpty />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button, Toast } from '@coze-arch/coze-design';
|
||||
|
||||
export const CopyPrompt = (props: {
|
||||
editor: EditorAPI;
|
||||
onCopyPrompt: () => void;
|
||||
}) => {
|
||||
const { editor, onCopyPrompt } = props;
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const text = editor?.$view.state.doc.toString();
|
||||
const result = copy(text, { format: 'text/plain' });
|
||||
result &&
|
||||
Toast.success(I18n.t('prompt_library_prompt_copied_successfully'));
|
||||
onCopyPrompt?.();
|
||||
}}
|
||||
>
|
||||
{I18n.t('prompt_detail_copy_prompt')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { CopyPrompt } from './copy-prompt';
|
||||
export { InsertToEditor } from './insert-to-editor';
|
||||
export { PromptDiff } from './prompt-diff';
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { insertToNewline } from '@coze-common/prompt-kit-base/shared';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
export const InsertToEditor = (props: {
|
||||
outerEditor: EditorAPI;
|
||||
prompt: string;
|
||||
onInsertPrompt: (prompt: string) => void;
|
||||
onCancel: (e: React.MouseEvent) => void;
|
||||
}) => {
|
||||
const { outerEditor, prompt, onInsertPrompt, onCancel } = props;
|
||||
return (
|
||||
<Button
|
||||
disabled={!prompt}
|
||||
onClick={async e => {
|
||||
const insertPrompt = await insertToNewline({
|
||||
editor: outerEditor,
|
||||
prompt,
|
||||
});
|
||||
onInsertPrompt(insertPrompt);
|
||||
onCancel?.(e);
|
||||
}}
|
||||
>
|
||||
{I18n.t('prompt_resource_insert_prompt')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
export const PromptDiff = (props: { onDiff: () => void }) => {
|
||||
const { onDiff } = props;
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
onDiff?.();
|
||||
}}
|
||||
>
|
||||
{I18n.t('compare_prompt_compare_debug')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
.prompt-library-modal {
|
||||
:global {
|
||||
.semi-modal-body {
|
||||
> div:first-child {
|
||||
> div:first-child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.semi-modal-footer {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useEditor } from '@coze-editor/editor/react';
|
||||
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozPlus } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
Modal,
|
||||
type ModalProps,
|
||||
Search,
|
||||
Button,
|
||||
} from '@coze-arch/coze-design';
|
||||
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
import { LibraryBlockWidget } from '@coze-common/editor-plugins/library-insert';
|
||||
import { InputSlotWidget } from '@coze-common/editor-plugins/input-slot';
|
||||
import {
|
||||
PromptEditorRender,
|
||||
PromptEditorProvider,
|
||||
} from '@coze-common/prompt-kit-base/editor';
|
||||
import { usePromptConfiguratorModal } from '@coze-common/prompt-kit-adapter/create-prompt';
|
||||
|
||||
import { type LibraryInfo, getLibraryListByCategory } from './library-request';
|
||||
import { LibraryList, type InfiniteListRef } from './library-list';
|
||||
import { CopyPrompt, InsertToEditor, PromptDiff } from './footer-actions';
|
||||
import { EmptyPrompt } from './empty';
|
||||
|
||||
import '@coze-common/prompt-kit-base/shared/css';
|
||||
import styles from './index.module.less';
|
||||
|
||||
const getTabLabelMap = (isPersonal: boolean) => ({
|
||||
Recommended: I18n.t('prompt_resource_recommended'),
|
||||
Team: isPersonal
|
||||
? I18n.t('prompt_resource_personal')
|
||||
: I18n.t('prompt_resource_team'),
|
||||
});
|
||||
const LIMIT_LIBRARY_SIZE = 15;
|
||||
|
||||
interface PromptContextInfo {
|
||||
botId?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
contextHistory?: string;
|
||||
}
|
||||
|
||||
export interface ActionExtraInfo {
|
||||
category: 'Recommended' | 'Team';
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface PromptLibraryProps extends ModalProps {
|
||||
spaceId: string;
|
||||
isPersonal?: boolean;
|
||||
editor: EditorAPI;
|
||||
getConversationId?: () => string | undefined;
|
||||
getPromptContextInfo?: () => PromptContextInfo;
|
||||
enableDiff?: boolean;
|
||||
importPromptWhenEmpty?: string;
|
||||
defaultActiveTab?: 'Recommended' | 'Team';
|
||||
tabs?: ('Recommended' | 'Team')[];
|
||||
/** 用于埋点: 页面来源 */
|
||||
source: string;
|
||||
/** 用于埋点: bot_id */
|
||||
botId?: string;
|
||||
/** 用于埋点: project_id */
|
||||
projectId?: string;
|
||||
/** 用于埋点: workflow_id */
|
||||
workflowId?: string;
|
||||
onInsertPrompt?: (prompt: string, selectedLibrary: ActionExtraInfo) => void;
|
||||
onUpdateSuccess?: (
|
||||
mode: 'create' | 'edit' | 'info',
|
||||
selectedLibrary: ActionExtraInfo,
|
||||
) => void;
|
||||
onCopyPrompt?: (selectedLibrary: ActionExtraInfo) => void;
|
||||
onDeletePrompt?: (selectedLibrary: ActionExtraInfo) => void;
|
||||
onDiff?: ({
|
||||
prompt,
|
||||
libraryId,
|
||||
}: {
|
||||
prompt: string;
|
||||
libraryId: string;
|
||||
}) => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
export const PromptLibrary = ({
|
||||
spaceId,
|
||||
onCancel,
|
||||
defaultActiveTab = 'Recommended',
|
||||
getConversationId,
|
||||
getPromptContextInfo,
|
||||
editor: outerEditor,
|
||||
source,
|
||||
tabs = ['Recommended', 'Team'],
|
||||
botId,
|
||||
projectId,
|
||||
workflowId,
|
||||
isPersonal = false,
|
||||
importPromptWhenEmpty,
|
||||
enableDiff = false,
|
||||
onInsertPrompt,
|
||||
onUpdateSuccess,
|
||||
onCopyPrompt,
|
||||
onDeletePrompt,
|
||||
onDiff,
|
||||
}: PromptLibraryProps) => {
|
||||
const [activeTab, setActiveTab] = useState<'Recommended' | 'Team'>(
|
||||
defaultActiveTab,
|
||||
);
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [dataList, setDataList] = useState<LibraryInfo[]>([]);
|
||||
const [selectedLibraryId, setSelectedLibraryId] = useState<string>('');
|
||||
const [searchWord, setSearchWord] = useState<string | undefined>(undefined);
|
||||
const targetRef = useRef<HTMLDivElement>(null);
|
||||
const listRef = useRef<InfiniteListRef<LibraryInfo>>(null);
|
||||
const editor = useEditor<EditorAPI>();
|
||||
const isEmptyList = !isLoading && dataList.length === 0;
|
||||
|
||||
const { open: openPromptConfiguratorModal, node: PromptConfiguratorModal } =
|
||||
usePromptConfiguratorModal({
|
||||
spaceId,
|
||||
isPersonal,
|
||||
enableDiff,
|
||||
getConversationId,
|
||||
getPromptContextInfo,
|
||||
importPromptWhenEmpty,
|
||||
source,
|
||||
botId,
|
||||
projectId,
|
||||
workflowId,
|
||||
onDiff,
|
||||
onUpdateSuccess: (mode, id) => {
|
||||
if (tabs.includes(activeTab)) {
|
||||
setActiveTab('Team');
|
||||
}
|
||||
onUpdateSuccess?.(mode, {
|
||||
id: id || '',
|
||||
category: 'Team',
|
||||
});
|
||||
listRef.current?.reload();
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
editor.$view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.$view.state.doc.length,
|
||||
insert: prompt,
|
||||
},
|
||||
});
|
||||
}, [editor, prompt]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedLibraryId || isLoading || !dataList.length) {
|
||||
return;
|
||||
}
|
||||
const selectedLibrary = dataList.find(
|
||||
item => item.id === selectedLibraryId,
|
||||
);
|
||||
if (!selectedLibrary) {
|
||||
return;
|
||||
}
|
||||
const { promptText } = selectedLibrary;
|
||||
|
||||
if (promptText) {
|
||||
setPrompt(promptText);
|
||||
return;
|
||||
}
|
||||
PlaygroundApi.GetPromptResourceInfo({
|
||||
prompt_resource_id: selectedLibraryId,
|
||||
}).then(({ data: { prompt_text: newPrompt } = {} }) => {
|
||||
setPrompt(newPrompt ?? '');
|
||||
});
|
||||
}, [selectedLibraryId, dataList, isLoading]);
|
||||
|
||||
// 切换tab、无选中提示词,重置搜索词
|
||||
useEffect(() => {
|
||||
setSelectedLibraryId('');
|
||||
setPrompt('');
|
||||
}, [activeTab, isEmptyList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={I18n.t('prompt_library_prompt_library')}
|
||||
visible
|
||||
className={styles['prompt-library-modal']}
|
||||
width="880px"
|
||||
closeOnEsc={false}
|
||||
maskClosable={false}
|
||||
footer={
|
||||
<div className="flex justify-end">
|
||||
{enableDiff ? (
|
||||
<PromptDiff
|
||||
onDiff={() => {
|
||||
onDiff?.({ prompt, libraryId: selectedLibraryId });
|
||||
sendTeaEvent(EVENT_NAMES.prompt_library_front, {
|
||||
source,
|
||||
prompt_id: selectedLibraryId,
|
||||
space_id: spaceId,
|
||||
prompt_type: 'workspace',
|
||||
action: 'compare',
|
||||
bot_id: botId,
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
sendTeaEvent(EVENT_NAMES.compare_mode_front, {
|
||||
source,
|
||||
action: 'start',
|
||||
compare_type: 'prompts',
|
||||
bot_id: botId,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<CopyPrompt
|
||||
editor={editor}
|
||||
onCopyPrompt={() => {
|
||||
onCopyPrompt?.({ id: selectedLibraryId, category: activeTab });
|
||||
sendTeaEvent(EVENT_NAMES.prompt_library_front, {
|
||||
source,
|
||||
prompt_id: selectedLibraryId,
|
||||
space_id: spaceId,
|
||||
prompt_type: 'workspace',
|
||||
action: 'copy',
|
||||
bot_id: botId,
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<InsertToEditor
|
||||
outerEditor={outerEditor}
|
||||
prompt={prompt}
|
||||
onInsertPrompt={insertPrompt => {
|
||||
onInsertPrompt?.(insertPrompt, {
|
||||
id: selectedLibraryId,
|
||||
category: activeTab,
|
||||
});
|
||||
sendTeaEvent(EVENT_NAMES.prompt_library_front, {
|
||||
source,
|
||||
prompt_id: selectedLibraryId,
|
||||
space_id: spaceId,
|
||||
prompt_type: 'workspace',
|
||||
action: 'insert',
|
||||
bot_id: botId,
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
}}
|
||||
onCancel={() => {
|
||||
onCancel?.();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<div className="flex flex-col gap-5 overflow-hidden h-[620px]">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-3 pl-3">
|
||||
{tabs.map(category => (
|
||||
<div
|
||||
key={category}
|
||||
className={classNames(
|
||||
'coz-fg-secondary text-sm cursor-pointer font-medium',
|
||||
{
|
||||
'!coz-fg-hglt': activeTab === category,
|
||||
},
|
||||
)}
|
||||
onClick={() => {
|
||||
setActiveTab(category);
|
||||
}}
|
||||
>
|
||||
{getTabLabelMap(isPersonal)[category]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Search
|
||||
className="w-[192px]"
|
||||
placeholder={I18n.t('Search')}
|
||||
onSearch={setSearchWord}
|
||||
/>
|
||||
{isEmptyList ? null : (
|
||||
<Button
|
||||
type="primary"
|
||||
className="!coz-mg-hglt !coz-fg-hglt hover:!coz-mg-hglt-hovered active:!coz-mg-hglt-pressed"
|
||||
icon={<IconCozPlus />}
|
||||
onClick={() => {
|
||||
openPromptConfiguratorModal({
|
||||
mode: 'create',
|
||||
});
|
||||
}}
|
||||
>
|
||||
{I18n.t('prompt_library_new_prompt')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 overflow-hidden flex-1">
|
||||
<div
|
||||
className="flex-1 basis-1/3 overflow-y-auto styled-scrollbar hover-show-scrollbar"
|
||||
ref={targetRef}
|
||||
>
|
||||
<LibraryList
|
||||
ref={listRef}
|
||||
targetRef={targetRef}
|
||||
spaceId={spaceId}
|
||||
category={activeTab}
|
||||
size={LIMIT_LIBRARY_SIZE}
|
||||
searchWord={searchWord}
|
||||
getData={getLibraryListByCategory}
|
||||
onChangeState={(newIsLoading, newDataList) => {
|
||||
setIsLoading(newIsLoading);
|
||||
setDataList(newDataList);
|
||||
}}
|
||||
onActive={id => {
|
||||
setSelectedLibraryId(id);
|
||||
}}
|
||||
onDeleteAction={id => {
|
||||
PlaygroundApi.DeletePromptResource({
|
||||
prompt_resource_id: id,
|
||||
}).then(() => {
|
||||
onDeletePrompt?.({ id, category: 'Team' });
|
||||
sendTeaEvent(EVENT_NAMES.prompt_library_front, {
|
||||
source,
|
||||
prompt_id: id,
|
||||
space_id: spaceId,
|
||||
prompt_type: 'workspace',
|
||||
action: 'delete',
|
||||
bot_id: botId,
|
||||
project_id: projectId,
|
||||
workflow_id: workflowId,
|
||||
});
|
||||
listRef.current?.reload();
|
||||
});
|
||||
}}
|
||||
onEditAction={id => {
|
||||
openPromptConfiguratorModal({
|
||||
mode: 'edit',
|
||||
editId: id,
|
||||
});
|
||||
}}
|
||||
onEmptyClick={() => {
|
||||
openPromptConfiguratorModal({
|
||||
mode: 'create',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 basis-2/3 coz-bg-max rounded-normal border-solid coz-stroke-primary border-[0.5px] p-2 empty:hidden overflow-y-auto styled-scrollbar hover-show-scrollbar">
|
||||
{isEmptyList ? null : (
|
||||
<>
|
||||
{prompt ? (
|
||||
<div className="relative ">
|
||||
<PromptEditorRender defaultValue={prompt} readonly />
|
||||
<InputSlotWidget mode="input" />
|
||||
<LibraryBlockWidget
|
||||
librarys={[]}
|
||||
readonly
|
||||
spaceId={spaceId}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyPrompt className="flex justify-center items-center h-full w-full" />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
{PromptConfiguratorModal}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePromptLibraryModal = (
|
||||
props: Omit<PromptLibraryProps, 'onClose'>,
|
||||
) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [dynamicProps, setDynamicProps] = useState<Partial<PromptLibraryProps>>(
|
||||
{},
|
||||
);
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
const open = (options?: Partial<PromptLibraryProps>) => {
|
||||
setVisible(true);
|
||||
setDynamicProps(options ?? {});
|
||||
};
|
||||
return {
|
||||
node: visible ? (
|
||||
<PromptEditorProvider>
|
||||
<PromptLibrary {...props} {...dynamicProps} onCancel={close} />
|
||||
</PromptEditorProvider>
|
||||
) : null,
|
||||
close,
|
||||
open,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozWarningCircle } from '@coze-arch/coze-design/icons';
|
||||
import { EmptyState, Spin } from '@coze-arch/coze-design';
|
||||
|
||||
import { type EmptyProps } from './type';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
/* Plugin header */
|
||||
|
||||
function Index(props: EmptyProps) {
|
||||
const {
|
||||
isLoading,
|
||||
loadRetry,
|
||||
isError,
|
||||
renderEmpty,
|
||||
text,
|
||||
btn,
|
||||
icon,
|
||||
className,
|
||||
size,
|
||||
} = props;
|
||||
return (
|
||||
<div className={s['height-whole-100']}>
|
||||
{renderEmpty?.(props) ||
|
||||
(!isError ? (
|
||||
isLoading ? (
|
||||
<Spin
|
||||
tip={
|
||||
<span className={s['loading-text']}>{I18n.t('Loading')}</span>
|
||||
}
|
||||
wrapperClassName={s.spin}
|
||||
size="middle"
|
||||
/>
|
||||
) : (
|
||||
<div className={className}>
|
||||
<EmptyState
|
||||
title={text?.emptyTitle || I18n.t('inifinit_list_empty_title')}
|
||||
size={size}
|
||||
description={text?.emptyDesc || ''}
|
||||
buttonText={btn?.emptyText}
|
||||
buttonProps={btn?.emptyButtonProps}
|
||||
onButtonClick={btn?.emptyClick}
|
||||
icon={icon}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className={className}>
|
||||
<EmptyState
|
||||
className={s['load-fail']}
|
||||
title={I18n.t('inifinit_list_load_fail')}
|
||||
icon={<IconCozWarningCircle />}
|
||||
buttonText={loadRetry && I18n.t('inifinit_list_retry')}
|
||||
onButtonClick={() => {
|
||||
loadRetry?.();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Spin, Button } from '@coze-arch/coze-design';
|
||||
|
||||
import { type FooterProps } from './type';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
/* Plugin header */
|
||||
|
||||
function Index(props: FooterProps) {
|
||||
const {
|
||||
isLoading,
|
||||
loadRetry,
|
||||
isError,
|
||||
renderFooter,
|
||||
isNeedBtnLoadMore,
|
||||
noMore,
|
||||
} = props;
|
||||
return (
|
||||
<div className={classNames(s['footer-container'], 'empty:hidden')}>
|
||||
{renderFooter?.(props) ||
|
||||
(isLoading ? (
|
||||
<>
|
||||
<Spin />
|
||||
<span className={s.loading}>{I18n.t('Loading')}</span>
|
||||
</>
|
||||
) : isError ? (
|
||||
<>
|
||||
<Spin />
|
||||
<span className={s['error-retry']} onClick={loadRetry}>
|
||||
{I18n.t('inifinit_list_retry')}
|
||||
</span>
|
||||
</>
|
||||
) : isNeedBtnLoadMore && !noMore ? (
|
||||
<Button
|
||||
onClick={loadRetry}
|
||||
className={s['load-more-btn']}
|
||||
theme="borderless"
|
||||
>
|
||||
{I18n.t('mkpl_load_btn')}
|
||||
</Button>
|
||||
) : null)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
||||
@@ -0,0 +1,111 @@
|
||||
.footer-container {
|
||||
padding: 12px 0 28px;
|
||||
text-align: center;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error-retry {
|
||||
margin-left: 10px;
|
||||
line-height: 20px;
|
||||
color: var(--semi-color-text-3, rgba(29, 28, 35, 35%));
|
||||
}
|
||||
|
||||
.error-retry {
|
||||
cursor: pointer;
|
||||
color: var(--semi-color-focus-border, #4D53E8);
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-spin-middle>.semi-spin-wrapper {
|
||||
height: 16px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.load-more-btn {
|
||||
font-weight: 600;
|
||||
background: #FFF;
|
||||
border-radius: 40px;
|
||||
|
||||
span {
|
||||
color: #1D1C23;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #FFF;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.responseive-foot-container {
|
||||
padding: 0 0 16px;
|
||||
|
||||
.load-more-btn {
|
||||
height: 40px;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.height-whole-100 {
|
||||
overflow: visible;
|
||||
height: 100%;
|
||||
|
||||
|
||||
|
||||
.spin {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
:global {
|
||||
.semi-spin-wrapper {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.semi-tabs-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.semi-spin-children {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-left: 8px;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: var(--semi-color-text-3, rgba(29, 28, 35, 35%));
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-list-item {
|
||||
padding: 0
|
||||
}
|
||||
|
||||
.semi-list-footer {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
type RefObject,
|
||||
useEffect,
|
||||
type ForwardedRef,
|
||||
} from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { List } from '@coze-arch/coze-design';
|
||||
|
||||
import useScroll from './use-scroll';
|
||||
import { type InfiniteListProps, type InfiniteListRef } from './type';
|
||||
import Footer from './footer';
|
||||
import Empty from './empty';
|
||||
|
||||
import s from './index.module.less';
|
||||
export type { InfiniteListRef };
|
||||
|
||||
// modify from packages/community/components/src/infinite-list/index.tsx
|
||||
function Index<T extends object>(
|
||||
props: InfiniteListProps<T>,
|
||||
ref: ForwardedRef<InfiniteListRef<T>>,
|
||||
) {
|
||||
const {
|
||||
className,
|
||||
grid,
|
||||
renderItem,
|
||||
itemClassName,
|
||||
renderFooter,
|
||||
scrollConf,
|
||||
onChangeState,
|
||||
isNeedBtnLoadMore = false,
|
||||
retryFunc,
|
||||
containerClassName,
|
||||
emptyConf,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
dataList,
|
||||
isLoading,
|
||||
loadMore,
|
||||
noMore,
|
||||
isLoadingError,
|
||||
reload,
|
||||
getDataList,
|
||||
} = useScroll<T>({ ...scrollConf, isNeedBtnLoadMore });
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
reload,
|
||||
getDataList,
|
||||
}),
|
||||
[reload, getDataList],
|
||||
);
|
||||
useEffect(() => {
|
||||
onChangeState?.(!!isLoading, dataList ?? []);
|
||||
}, [dataList, isLoading]);
|
||||
|
||||
return (
|
||||
<div className={cls(s['height-whole-100'], containerClassName)}>
|
||||
{!dataList?.length ? (
|
||||
/** 数据为空的时候,操作如何显示空页面 */
|
||||
<Empty
|
||||
isError={isLoadingError}
|
||||
isLoading={isLoading}
|
||||
loadRetry={retryFunc || loadMore}
|
||||
{...emptyConf}
|
||||
/>
|
||||
) : (
|
||||
<List
|
||||
{...{ className, grid }}
|
||||
emptyContent={<></>}
|
||||
dataSource={dataList}
|
||||
split={false}
|
||||
renderItem={(item, number) => (
|
||||
<List.Item
|
||||
className={
|
||||
typeof itemClassName === 'string'
|
||||
? itemClassName
|
||||
: itemClassName?.(item) // 支持动态行className
|
||||
}
|
||||
>
|
||||
{renderItem?.(item, number)}
|
||||
</List.Item>
|
||||
)}
|
||||
footer={
|
||||
<Footer
|
||||
isError={isLoadingError}
|
||||
noMore={noMore}
|
||||
isLoading={isLoading}
|
||||
loadRetry={retryFunc || loadMore}
|
||||
renderFooter={renderFooter}
|
||||
isNeedBtnLoadMore={isNeedBtnLoadMore}
|
||||
dataNum={dataList?.length}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const InfiniteList = forwardRef(Index) as <T>(
|
||||
props: InfiniteListProps<T> & { ref?: RefObject<InfiniteListRef<T>> },
|
||||
) => JSX.Element;
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactElement, type RefObject } from 'react';
|
||||
|
||||
import {
|
||||
type ListProps,
|
||||
type EmptyStateProps,
|
||||
type ButtonProps,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
export interface EmptyProps {
|
||||
className?: string;
|
||||
isError?: boolean;
|
||||
isLoading?: boolean;
|
||||
loadRetry?: () => void; //重试加载
|
||||
size?: EmptyStateProps['size'];
|
||||
text?: {
|
||||
emptyTitle?: string;
|
||||
emptyDesc?: string;
|
||||
searchEmptyTitle?: string;
|
||||
};
|
||||
btn?: {
|
||||
emptyClick?: () => void; //
|
||||
emptyText?: string;
|
||||
emptyButtonProps?: ButtonProps;
|
||||
};
|
||||
icon?: ReactElement;
|
||||
|
||||
renderEmpty?: (
|
||||
emptyProps: Omit<EmptyProps, 'renderEmpty'>,
|
||||
) => React.ReactNode | null;
|
||||
}
|
||||
export interface FooterProps {
|
||||
isError?: boolean; // 是否加载出错
|
||||
isLoading?: boolean; // 是否加载中
|
||||
noMore?: boolean; //没有更多数据
|
||||
isNeedBtnLoadMore?: boolean;
|
||||
dataNum?: number;
|
||||
loadRetry?: () => void; //重试加载
|
||||
renderFooter?: (
|
||||
footerProps: Omit<FooterProps, 'renderFooter'>,
|
||||
) => React.ReactNode | null;
|
||||
}
|
||||
|
||||
export interface InfiniteListDataProps<T> {
|
||||
list: T[];
|
||||
hasMore?: boolean;
|
||||
// nextPage: number;
|
||||
cursor: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ScrollProps<T> {
|
||||
threshold?: number; //距离下方多长距离,开始加载数据
|
||||
targetRef?: RefObject<HTMLDivElement>; // 监听滚动的Dom 引用
|
||||
loadData: (
|
||||
current: InfiniteListDataProps<T>,
|
||||
) => Promise<InfiniteListDataProps<T>>; // 加载更多数据
|
||||
reloadDeps?: unknown[]; // 重新加载数据依赖
|
||||
isNeedBtnLoadMore?: boolean;
|
||||
isLoading?: boolean; // 是否加载中
|
||||
resetDataIfReload?: boolean; // 当reload时,是否先reset列表已存在数据,默认为true
|
||||
}
|
||||
|
||||
export interface InfiniteListProps<T>
|
||||
extends Partial<
|
||||
Pick<ListProps<T>, 'className' | 'emptyContent' | 'grid' | 'renderItem'>
|
||||
> {
|
||||
containerClassName?: string;
|
||||
canShowData?: boolean; //是否能够显示数据了
|
||||
isSearching?: boolean; // 是否搜索中,主要是用于错误显示的时候,选择文案使用
|
||||
itemClassName?: string | ((item: T) => string);
|
||||
isNeedBtnLoadMore?: boolean;
|
||||
isResponsive?: boolean;
|
||||
emptyConf?: {
|
||||
className?: string;
|
||||
renderEmpty?: EmptyProps['renderEmpty'];
|
||||
text?: EmptyProps['text'];
|
||||
btn?: EmptyProps['btn'];
|
||||
icon?: EmptyProps['icon'];
|
||||
size?: EmptyProps['size'];
|
||||
};
|
||||
renderFooter?: FooterProps['renderFooter'];
|
||||
scrollConf: ScrollProps<T>;
|
||||
rowKey?: string;
|
||||
retryFunc?: () => void;
|
||||
onChangeState?: (loading: boolean, data: T[]) => void;
|
||||
}
|
||||
|
||||
export interface InfiniteListRef<T> {
|
||||
reload: () => void;
|
||||
getDataList: () => T[]; // 获取当前列表数据
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
useState,
|
||||
useRef,
|
||||
useEffect,
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
useInfiniteScroll,
|
||||
useUpdateEffect,
|
||||
useMemoizedFn,
|
||||
useDebounceFn,
|
||||
} from 'ahooks';
|
||||
|
||||
import { type ScrollProps, type InfiniteListDataProps } from './type';
|
||||
|
||||
/* 滚动Hooks */
|
||||
|
||||
function useForwardFunc<T>(
|
||||
dataInfo: InfiniteListDataProps<T>,
|
||||
mutate: Dispatch<SetStateAction<InfiniteListDataProps<T>>>,
|
||||
) {
|
||||
// 手动插入数据,不通过接口
|
||||
const insertData = (item: T, index: number) => {
|
||||
dataInfo.list.splice(index, 0, item);
|
||||
mutate({
|
||||
...dataInfo,
|
||||
list: [...dataInfo.list],
|
||||
});
|
||||
};
|
||||
|
||||
// 手动删除数据,不通过接口
|
||||
const removeData = (index: number) => {
|
||||
dataInfo.list.splice(index, 1);
|
||||
mutate({
|
||||
...dataInfo,
|
||||
list: [...dataInfo.list],
|
||||
});
|
||||
};
|
||||
|
||||
const getDataList = () => dataInfo?.list;
|
||||
|
||||
return { insertData, removeData, getDataList };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function, @coze-arch/max-line-per-function -- 看了下代码行数不太好优化
|
||||
function useScroll<T>(props: ScrollProps<T>) {
|
||||
const {
|
||||
targetRef,
|
||||
loadData,
|
||||
threshold,
|
||||
reloadDeps,
|
||||
isNeedBtnLoadMore,
|
||||
resetDataIfReload = true,
|
||||
} = props;
|
||||
const [isLoadingError, setIsLoadingError] = useState<boolean>(false);
|
||||
const refFetchNo = useRef<number>(0);
|
||||
const refResolve = useRef<(value: InfiniteListDataProps<T>) => void>();
|
||||
const {
|
||||
loading,
|
||||
data: dataInfo,
|
||||
loadingMore,
|
||||
loadMore,
|
||||
noMore,
|
||||
cancel,
|
||||
mutate,
|
||||
reload,
|
||||
} = useInfiniteScroll<InfiniteListDataProps<T>>(
|
||||
async current => {
|
||||
// 此处逻辑如此复杂,是解决Scroll中的bug。
|
||||
// useInfiniteScroll中的cancel只是取消了一次请求,但是数据会根据current重新设置一遍。
|
||||
const defaultData = {
|
||||
cursor: '0',
|
||||
list: [],
|
||||
};
|
||||
const fetchNo = refFetchNo.current;
|
||||
if (refResolve.current) {
|
||||
// 保证顺序执行,如果有当前方法,就取消上一次的请求,防止出现由于网络原因导致数据覆盖问题
|
||||
// 同时发出A1,A2,三次请求,但是A1先到达,然后请求了B1, 但是A1过慢,导致了A1覆盖了B1的请求。
|
||||
refResolve.current({
|
||||
...defaultData,
|
||||
...(current || {}),
|
||||
});
|
||||
}
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
refResolve.current = resolve;
|
||||
loadData(current || defaultData)
|
||||
.then(value => resolve(value))
|
||||
.catch(err => reject(err));
|
||||
});
|
||||
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
refResolve.current = null;
|
||||
|
||||
// 切换Tab的时候,如果此时正在请求,防止数据的残留界面显示
|
||||
if (refFetchNo.current !== fetchNo) {
|
||||
if (current) {
|
||||
current.list = [];
|
||||
}
|
||||
return {
|
||||
list: [],
|
||||
cursor: '0',
|
||||
};
|
||||
}
|
||||
return result as InfiniteListDataProps<T>;
|
||||
},
|
||||
{
|
||||
target: isLoadingError || isNeedBtnLoadMore ? null : targetRef, //失败的时候,通过去掉target的事件绑定,禁止滚动加载。
|
||||
threshold,
|
||||
onBefore: () => {
|
||||
//setIsLoadingError(false);
|
||||
},
|
||||
isNoMore: data => data?.hasMore !== undefined && !data?.hasMore,
|
||||
onSuccess: () => {
|
||||
if (isLoadingError) {
|
||||
setIsLoadingError(false);
|
||||
}
|
||||
},
|
||||
onError: e => {
|
||||
// 如果在请求第一页数据时发生错误,并且当前列表不为空,则reset数据
|
||||
// 这个case只有当resetDataIfReload设置为false时才会发生
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
if (dataInfo.cursor === '0' && (dataInfo?.list?.length ?? 0) > 0) {
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
mutate({
|
||||
...dataInfo,
|
||||
list: [],
|
||||
});
|
||||
}
|
||||
setIsLoadingError(true);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { insertData, removeData, getDataList } = useForwardFunc(
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
dataInfo,
|
||||
mutate,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isNeedBtnLoadMore && !(loading || loadingMore)) {
|
||||
reload();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const reloadData = useMemoizedFn(() => {
|
||||
mutate({
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
list: resetDataIfReload ? [] : dataInfo.list,
|
||||
hasMore: undefined,
|
||||
cursor: '0',
|
||||
});
|
||||
cancel();
|
||||
setIsLoadingError(false);
|
||||
reload();
|
||||
});
|
||||
|
||||
useUpdateEffect(() => {
|
||||
refFetchNo.current++;
|
||||
reloadData();
|
||||
}, [...(reloadDeps || [])]);
|
||||
const isLoading = loading || loadingMore || props.isLoading;
|
||||
const { run: loadMoreDebounce } = useDebounceFn(
|
||||
() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
if (!isNeedBtnLoadMore) {
|
||||
loadMore();
|
||||
}
|
||||
},
|
||||
{ wait: 500 },
|
||||
);
|
||||
useEffect(() => {
|
||||
const resize = () => {
|
||||
loadMoreDebounce();
|
||||
};
|
||||
window.addEventListener('resize', resize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', resize);
|
||||
};
|
||||
}, []);
|
||||
const { list } = dataInfo || {};
|
||||
return {
|
||||
dataList: list,
|
||||
isLoading,
|
||||
loadMore: () => {
|
||||
if (!isLoading) {
|
||||
//如果已经有数据加载中了,需要禁止重复加载。
|
||||
loadMore();
|
||||
}
|
||||
},
|
||||
reload: reloadData,
|
||||
noMore,
|
||||
cancel,
|
||||
isLoadingError,
|
||||
mutate,
|
||||
insertData,
|
||||
removeData,
|
||||
getDataList,
|
||||
};
|
||||
}
|
||||
|
||||
export default useScroll;
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { ActionKey } from '@coze-arch/idl/resource';
|
||||
import { type ResourceAction } from '@coze-arch/idl/plugin_develop';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozMore } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip, Typography } from '@coze-arch/coze-design';
|
||||
const { Text } = Typography;
|
||||
|
||||
export interface LibraryItemProps {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
isSelected?: boolean;
|
||||
onActive?: (id: string) => void;
|
||||
actions?: ResourceAction[];
|
||||
onDeleteAction?: (id: string) => void;
|
||||
onEditAction?: (id: string) => void;
|
||||
}
|
||||
const actionsMap: {
|
||||
[key in ActionKey.Delete | ActionKey.Edit]: string;
|
||||
} = {
|
||||
[ActionKey.Delete]: I18n.t('Delete'),
|
||||
[ActionKey.Edit]: I18n.t('Edit'),
|
||||
};
|
||||
|
||||
export const LibraryItem = ({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
isSelected,
|
||||
onActive,
|
||||
onDeleteAction,
|
||||
onEditAction,
|
||||
}: LibraryItemProps) => {
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
const handleActions = (action: ActionKey) => {
|
||||
if (action === ActionKey.Delete) {
|
||||
onDeleteAction?.(id);
|
||||
return;
|
||||
}
|
||||
if (action === ActionKey.Edit) {
|
||||
onEditAction?.(id);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cls(
|
||||
'w-full flex flex-row justify-between items-center overflow-hidden px-3 h-[64px]',
|
||||
'relative',
|
||||
'after:content-[""] after:absolute after:left-0 after:right-0',
|
||||
'after:bottom-0 after:h-[1px] after:coz-mg-primary',
|
||||
'hover:coz-mg-secondary-hovered hover:rounded hover:after:hidden',
|
||||
'cursor-pointer',
|
||||
{
|
||||
'rounded coz-mg-primary after:hidden': isSelected,
|
||||
},
|
||||
)}
|
||||
onClick={() => {
|
||||
onActive?.(id);
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setIsHover(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsHover(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-1 min-w-[0px] w-0 flex-col gap-[2px]">
|
||||
<Text className="text-lg flex-1 font-medium" ellipsis>
|
||||
{title}
|
||||
</Text>
|
||||
<Text
|
||||
className="text-base"
|
||||
ellipsis={{
|
||||
rows: 1,
|
||||
showTooltip: {
|
||||
opts: {
|
||||
position: 'right',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</Text>
|
||||
</div>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
className="!p-1"
|
||||
content={
|
||||
<div className="flex flex-col gap-[2px] w-[120px]">
|
||||
{actions
|
||||
?.filter(action => action.enable)
|
||||
?.filter(action => action.key in actionsMap)
|
||||
.map(action => (
|
||||
<div
|
||||
key={action.key}
|
||||
onClick={() => handleActions(action.key as ActionKey)}
|
||||
className="w-full text-sm h-[32px] p-2 flex items-center cursor-pointer hover:coz-mg-primary-hovered hover:rounded-mini"
|
||||
>
|
||||
{actionsMap[action.key as keyof typeof actionsMap]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cls(
|
||||
'w-6 h-6 rounded-little coz-mg-secondary-hovered flex items-center justify-center',
|
||||
{
|
||||
hidden: !actions?.length || !isHover,
|
||||
},
|
||||
)}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<IconCozMore />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
useEffect,
|
||||
type RefObject,
|
||||
useState,
|
||||
useRef,
|
||||
type ForwardedRef,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
} from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozPlus } from '@coze-arch/coze-design/icons';
|
||||
|
||||
import EmptyLibraryIcon from '../assets/empty-library-icon.svg';
|
||||
import {
|
||||
type LibraryInfo,
|
||||
type LibraryListRequest,
|
||||
type LibraryListResponse,
|
||||
} from './library-request';
|
||||
import { LibraryItem } from './library-item';
|
||||
import { InfiniteList, type InfiniteListRef } from './infinite-list';
|
||||
export type { InfiniteListRef };
|
||||
interface LibraryListProps {
|
||||
searchWord?: string | undefined;
|
||||
category: 'Recommended' | 'Team';
|
||||
spaceId: string;
|
||||
size: number;
|
||||
targetRef: RefObject<HTMLDivElement>;
|
||||
onActive: (id: string) => void;
|
||||
onEditAction: (id: string) => void;
|
||||
onDeleteAction: (id: string) => void;
|
||||
getData: (req: LibraryListRequest) => Promise<LibraryListResponse>;
|
||||
onChangeState?: (isLoading: boolean, dataList: LibraryInfo[]) => void;
|
||||
onEmptyClick?: () => void;
|
||||
}
|
||||
|
||||
export const Index = (
|
||||
props: LibraryListProps,
|
||||
ref: ForwardedRef<InfiniteListRef<LibraryInfo>>,
|
||||
) => {
|
||||
const {
|
||||
getData,
|
||||
onActive,
|
||||
searchWord,
|
||||
category,
|
||||
targetRef,
|
||||
size,
|
||||
onChangeState,
|
||||
onDeleteAction,
|
||||
onEditAction,
|
||||
spaceId,
|
||||
onEmptyClick,
|
||||
} = props;
|
||||
const listRef = useRef<InfiniteListRef<LibraryInfo>>(null);
|
||||
const [dataList, setDataList] = useState<LibraryInfo[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedLibraryId, setSelectedLibraryId] = useState<string>('');
|
||||
|
||||
// 切换tab,默认选中第一个
|
||||
useEffect(() => {
|
||||
if (!dataList.length || isLoading) {
|
||||
return;
|
||||
}
|
||||
const firstLibraryId = dataList[0].id;
|
||||
setSelectedLibraryId(firstLibraryId);
|
||||
onActive?.(firstLibraryId);
|
||||
}, [dataList, isLoading]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
reload: listRef.current?.reload ?? (() => void 0),
|
||||
getDataList: listRef.current?.getDataList ?? (() => []),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
return (
|
||||
<InfiniteList<LibraryInfo>
|
||||
ref={listRef}
|
||||
isNeedBtnLoadMore={false}
|
||||
onChangeState={(newIsLoading, newDataList) => {
|
||||
onChangeState?.(newIsLoading, newDataList);
|
||||
setDataList(newDataList);
|
||||
setIsLoading(newIsLoading);
|
||||
}}
|
||||
renderItem={(card: LibraryInfo) => (
|
||||
<LibraryItem
|
||||
key={card.id}
|
||||
id={card.id}
|
||||
title={card.name}
|
||||
description={card.description}
|
||||
actions={card.actions}
|
||||
isSelected={selectedLibraryId === card.id}
|
||||
onActive={id => {
|
||||
onActive?.(id);
|
||||
setSelectedLibraryId(id);
|
||||
}}
|
||||
onEditAction={id => {
|
||||
onEditAction?.(id);
|
||||
}}
|
||||
onDeleteAction={id => {
|
||||
onDeleteAction?.(id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
scrollConf={{
|
||||
reloadDeps: [searchWord, category],
|
||||
targetRef,
|
||||
loadData: current =>
|
||||
getData({
|
||||
cursor: current?.cursor ?? '0',
|
||||
searchWord: searchWord ?? '',
|
||||
category,
|
||||
spaceId,
|
||||
size,
|
||||
}),
|
||||
}}
|
||||
emptyConf={{
|
||||
className: 'flex flex-col items-center justify-center h-full',
|
||||
icon: <img src={EmptyLibraryIcon} alt="empty-library" />,
|
||||
size: 'full_screen',
|
||||
text: {
|
||||
emptyTitle: I18n.t('prompt_library_empty_title'),
|
||||
emptyDesc: I18n.t('prompt_library_empty_describe'),
|
||||
},
|
||||
btn: {
|
||||
emptyText: I18n.t('prompt_library_new_prompt'),
|
||||
emptyButtonProps: {
|
||||
type: 'primary',
|
||||
className:
|
||||
'!coz-mg-hglt !coz-fg-hglt hover:!coz-mg-hglt-hovered active:!coz-mg-hglt-pressed',
|
||||
icon: <IconCozPlus />,
|
||||
onClick: () => {
|
||||
onEmptyClick?.();
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const LibraryList = forwardRef(Index) as <T>(
|
||||
props: LibraryListProps & { ref?: RefObject<InfiniteListRef<T>> },
|
||||
) => JSX.Element;
|
||||