feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -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,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user