diff --git a/components/PromptModal.tsx b/components/PromptModal.tsx index a302810..346f9c1 100644 --- a/components/PromptModal.tsx +++ b/components/PromptModal.tsx @@ -5,7 +5,7 @@ import reactToolCssText from 'data-text:rc-tooltip/assets/bootstrap_white.css' import cssText from 'data-text:~style.css' import kebabCase from 'lodash/kebabCase' import Tooltip from 'rc-tooltip' -import React, { Fragment, useMemo, useState } from 'react' +import React, { Fragment, useEffect, useMemo, useState } from 'react' import { useStorage } from '@plasmohq/storage/hook' @@ -15,7 +15,8 @@ import { DisplayRow, DisplayRowType, PROMOT_KEY, - Row + Row, + fetchPromotWithRetry } from '../request' import { CopyIcon } from './CopyIcon' import { PromptEditor } from './PromptEditor' @@ -54,20 +55,17 @@ export const PromptModal: React.FC<{ [] ) - const [ - remotePrompts, - _un, - { - setStoreValue: setRemotePromptsStoreValue, - setRenderValue: setRemotePromptsRenderValue + const [remotePrompts, setRemotePrompts] = useState([]) + + useEffect(() => { + const initData = async () => { + const value = await fetchPromotWithRetry() + if (value) { + setRemotePrompts(value) + } } - ] = useStorage( - { - key: PROMOT_KEY, - area: 'local' - }, - [] - ) + initData() + }, []) const prompts = useMemo(() => { return [ diff --git a/contents/AutoComplete.css b/contents/AutoComplete.css index 5071476..dddf1d5 100644 --- a/contents/AutoComplete.css +++ b/contents/AutoComplete.css @@ -13,14 +13,13 @@ #chatgpt-prompt-helper-panel { position: absolute; - bottom: 0; + bottom: 2px; background: #ffffff; border-radius: 8px; border: solid 1px #dddddd; box-shadow: 1px 1px #ddd; max-height: 300px; width: 600px; - bottom: 51px; left: 2px; } diff --git a/contents/AutoComplete.tsx b/contents/AutoComplete.tsx index b3be861..4bc2d10 100644 --- a/contents/AutoComplete.tsx +++ b/contents/AutoComplete.tsx @@ -1,12 +1,13 @@ import reactToolCssText from 'data-text:rc-tooltip/assets/bootstrap_white.css' import cssText from 'data-text:~/contents/AutoComplete.css' +import debounce from 'lodash/debounce' import partition from 'lodash/partition' import snakeCase from 'lodash/snakeCase' import type { PlasmoContentScript, PlasmoGetInlineAnchor } from 'plasmo' import Tooltip from 'rc-tooltip' import React, { useEffect, useMemo, useRef, useState } from 'react' +import { createPortal } from 'react-dom' import { useLatest } from 'react-use' -import debounce from 'lodash/debounce' import { useStorage } from '@plasmohq/storage/hook' @@ -25,12 +26,14 @@ export const config: PlasmoContentScript = { matches: ['https://chat.openai.com/*'] } -export const getStyle = () => { +const getStyle = () => { const style = document.createElement('style') style.textContent = cssText + '\n' + reactToolCssText return style } +document.head.appendChild(getStyle()) + const getTextArea = async () => { try { const textArea = document.querySelector('textarea') @@ -38,12 +41,12 @@ const getTextArea = async () => { if (textArea.parentElement.nodeName !== 'DIV') { throw Error('') } - if (!textArea.placeholder.trim()) { + if (!textArea.placeholder?.trim()) { textArea.placeholder = `${ process.env.NODE_ENV === 'development' ? '' : '' }Try type / and see some helpful prompts. Powered by ChatGPT prompt helper` } - return textArea + return textArea.parentElement } } catch (error) { // console.error(error) @@ -84,16 +87,42 @@ const safeMath = (act: string, inputText: string) => { } } +const getContainerPosition = async () => { + const targetElement = await getTextArea() + const rect = targetElement.getBoundingClientRect() + + // Calculate positions relative to the document + // Calculate positions + const left = rect.left + window.scrollX + const top = rect.top + +window.scrollY + + return { left, top } +} + const AutoComplete = () => { const containerRef = useRef() const [inputText, setInputText] = useState() const [activeIndex, setActiveIndex] = useState(-1) const [hoverIndex, setHoverIndex] = useState(-1) const [panelVisible, setPanelVisible] = useState(false) + const [position, setPosition] = useState<{ top: number; left: number }>() - const [autoPromots, _un, { setStoreValue, setRenderValue }] = useStorage< - Row[] - >( + useEffect(() => { + const run = async () => { + console.log(await getContainerPosition()) + setPosition(await getContainerPosition()) + } + run() + window.addEventListener('resize', run) + + return () => { + window.removeEventListener('resize', run) + } + }, []) + + const [autoPromots, setAutoPromots] = useState([]) + + const [, _un, { setStoreValue, setRenderValue }] = useStorage( { key: PROMOT_KEY, area: 'local' @@ -185,8 +214,9 @@ const AutoComplete = () => { const initData = async () => { const value = await fetchPromotWithRetry() if (value) { - setRenderValue(value) - await setStoreValue(value) + setAutoPromots(value) + // setRenderValue(value) + await setStoreValue([]) } } initData() @@ -197,19 +227,23 @@ const AutoComplete = () => { 'textarea' ) as unknown as HTMLInputElement - - textarea?.addEventListener('input', debounce((e) => { - // @ts-ignore - const value = e.target.value - if (value?.startsWith('/')) { - setInputText(value) - setActiveIndex(0) - containerRef.current?.querySelector(`#${TOP_ANCHOR}`)?.scrollIntoView() - setPanelVisible(true) - } else { - setPanelVisible(false) - } - }, 200)) + textarea?.addEventListener( + 'input', + debounce((e) => { + // @ts-ignore + const value = e.target.value + if (value?.startsWith('/')) { + setInputText(value) + setActiveIndex(0) + containerRef.current + ?.querySelector(`#${TOP_ANCHOR}`) + ?.scrollIntoView() + setPanelVisible(true) + } else { + setPanelVisible(false) + } + }, 200) + ) const preventKeyboardEvent = (e: KeyboardEvent) => { e.cancelBubble = true @@ -325,9 +359,21 @@ const AutoComplete = () => { ) return ( -
- {childrenEl} -
+ <> + {createPortal( +
+ {childrenEl} +
, + document.body + )} + ) } diff --git a/package.json b/package.json index 6d38d08..706c729 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "chatgpt-prompt-helper", "displayName": "Chatgpt Prompt Helper", - "version": "1.0.3", + "version": "1.0.5", "description": "Chatgpt prompt, help you ask more efficiently", "author": "cottome", "scripts": { @@ -14,7 +14,7 @@ "@heroicons/react": "^2.0.16", "@plasmohq/storage": "^0.14.0", "@ungap/structured-clone": "^1.0.1", - "axios": "^1.2.3", + "axios": "^1.6.0", "downloadjs": "^1.4.7", "final-form": "^4.20.9", "html-to-image": "^1.11.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32333a9..4d0903c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^1.0.1 version: 1.0.1 axios: - specifier: ^1.2.3 - version: 1.2.3 + specifier: ^1.6.0 + version: 1.6.0 downloadjs: specifier: ^1.4.7 version: 1.4.7 @@ -2468,8 +2468,8 @@ packages: postcss-value-parser: 4.2.0 dev: true - /axios@1.2.3: - resolution: {integrity: sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==} + /axios@1.6.0: + resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 @@ -2932,6 +2932,7 @@ packages: /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + requiresBuild: true peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -3755,6 +3756,7 @@ packages: /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: safer-buffer: 2.1.2 dev: false @@ -4295,6 +4297,7 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + requiresBuild: true dev: false optional: true @@ -4586,6 +4589,7 @@ packages: /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + requiresBuild: true dev: false optional: true @@ -4836,6 +4840,7 @@ packages: /prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + requiresBuild: true dev: false optional: true @@ -5216,6 +5221,7 @@ packages: /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + requiresBuild: true dev: false optional: true