feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
Binary file not shown.
|
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 |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
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
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
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
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;
|
||||
Reference in New Issue
Block a user