Skip to content

Commit fe1db28

Browse files
committed
feat: toggle query text format
1 parent 5e00e3e commit fe1db28

File tree

6 files changed

+134
-10
lines changed

6 files changed

+134
-10
lines changed

src/components/ThreadAnchor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ const useThreadAnchorObserver = () => {
164164

165165
setWrapperPos({
166166
top: $messageContainer.offset()?.top || 0,
167-
left: $messageContainer.width()! + ($messageContainer.offset()?.left || 0),
167+
left:
168+
$messageContainer.width()! + ($messageContainer.offset()?.left || 0),
168169
});
169170

170171
setAnchorsProps([]);
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {
2+
Fragment,
3+
useEffect,
4+
useState,
5+
} from 'react';
6+
import ReactDOM from 'react-dom';
7+
8+
import $ from 'jquery';
9+
import { Text } from 'lucide-react';
10+
import { FaMarkdown } from 'react-icons/fa';
11+
import { useImmer } from 'use-immer';
12+
13+
import { ui } from '@/utils/ui';
14+
import { whereAmI } from '@/utils/utils';
15+
16+
import useElementObserver from './hooks/useElementObserver';
17+
import TooltipWrapper from './TooltipWrapper';
18+
19+
export default function ThreadQueryFormatSwitch() {
20+
const [containers, setContainers] = useState<
21+
{
22+
messageBlock: Element;
23+
query: Element;
24+
}[]
25+
>([]);
26+
27+
const [isMarkdown, setIsMarkdown] = useImmer<boolean[]>([]);
28+
29+
useEffect(() => {
30+
containers.forEach((container, index) => {
31+
if ($(container.query).find('#markdown-query-wrapper.tw-hidden').length) {
32+
setIsMarkdown((draft) => {
33+
draft[index] = false;
34+
});
35+
} else {
36+
setIsMarkdown((draft) => {
37+
draft[index] = true;
38+
});
39+
}
40+
});
41+
}, [containers, setIsMarkdown]);
42+
43+
useElementObserver({
44+
selector: () =>
45+
ui.getMessageBlocks().map(({ $query, $messageBlock }) => ({
46+
element: $query[0],
47+
args: { $messageBlock },
48+
})),
49+
callback: ({ element, args }) => {
50+
if (whereAmI() !== 'thread') return setContainers([]);
51+
52+
$(element).addClass('tw-relative');
53+
54+
setContainers((prev) => [
55+
...prev,
56+
{
57+
query: element,
58+
messageBlock: args.$messageBlock[0],
59+
},
60+
]);
61+
},
62+
observedIdentifier: 'thread-query-format-switch',
63+
});
64+
65+
useElementObserver({
66+
selector: () =>
67+
ui
68+
.getMessageBlocks()
69+
.map(
70+
({ $messageBlock }) =>
71+
$messageBlock.find('.whitespace-pre-line.break-words')[0]
72+
),
73+
callback: ({ element }) => {
74+
if (whereAmI() !== 'thread') return;
75+
76+
const isMarkdown =
77+
$(element).parent().find('#markdown-query-wrapper:not(.tw-hidden)')
78+
.length > 0 || !$(element).parent().find('#markdown-query-wrapper').length;
79+
80+
$(element).toggleClass('tw-hidden', isMarkdown);
81+
},
82+
observedIdentifier: 'append-tw-block-to-plain-text-block',
83+
});
84+
85+
return containers.map((container, index) => (
86+
<Fragment key={index}>
87+
{ReactDOM.createPortal(
88+
<div
89+
onClick={(e) => {
90+
e.stopPropagation();
91+
e.preventDefault();
92+
setIsMarkdown((draft) => {
93+
$(container.query)
94+
.find('.whitespace-pre-line.break-words')
95+
.toggleClass('tw-hidden', !draft[index]);
96+
$(container.query)
97+
.find('#markdown-query-wrapper')
98+
.toggleClass('tw-hidden', draft[index]);
99+
draft[index] = !draft[index];
100+
});
101+
}}
102+
className="tw-absolute tw-right-0 tw-top-0 tw-w-max tw-h-max tw-bg-secondary tw-text-secondary-foreground tw-p-2 tw-rounded-md tw-cursor-pointer tw-border tw-border-border tw-shadow-lg tw-opacity-10 hover:tw-opacity-100 tw-transition-all tw-animate-in tw-fade-in active:tw-scale-95"
103+
>
104+
<TooltipWrapper
105+
content={
106+
isMarkdown[index] ? 'Switch to Markdown' : 'Switch to Plain Text'
107+
}
108+
contentOptions={{
109+
sideOffset: 15,
110+
}}
111+
delayDuration={0}
112+
>
113+
{isMarkdown[index] ? (
114+
<Text className="tw-w-4 tw-h-4 tw-text-accent-foreground" />
115+
) : (
116+
<FaMarkdown className="tw-w-4 tw-h-4 tw-text-accent-foreground" />
117+
)}
118+
</TooltipWrapper>
119+
</div>,
120+
container.query
121+
)}
122+
</Fragment>
123+
));
124+
}

src/components/hooks/useElementObserver.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import {
55

66
import observer, { OnElementExistOptions } from '@/utils/observer';
77

8-
type useElementObserverProps = Omit<OnElementExistOptions<any>, 'callback'> & {
9-
callback?: ({ element }: { element: Element }) => void;
10-
};
8+
type useElementObserverProps = OnElementExistOptions<any>;
119

1210
export default function useElementObserver({
1311
selector,
@@ -20,9 +18,9 @@ export default function useElementObserver({
2018
useEffect(() => {
2119
const myObserver = observer.onElementExist({
2220
selector,
23-
callback: ({ element }) => {
24-
callback?.({ element });
25-
setContainer(element);
21+
callback: (args) => {
22+
callback?.(args);
23+
setContainer(args.element);
2624
},
2725
observedIdentifier,
2826
recurring,

src/content-script/Root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import useElementObserver from '@/components/hooks/useElementObserver';
66
import MainPage from '@/components/MainPage';
77
import QueryBox from '@/components/QueryBox';
88
import ThreadAnchor from '@/components/ThreadAnchor';
9+
import ThreadQueryFormatSwitch from '@/components/ThreadQueryFormatSwitch';
910
import { Toaster } from '@/components/ui/toaster';
1011
import { useToast } from '@/components/ui/use-toast';
1112
import {
@@ -32,6 +33,7 @@ export default function Root() {
3233
<QueryBox />
3334
<Commander />
3435
{popupSettingsStore.getState().qolTweaks.threadTOC && <ThreadAnchor />}
36+
{popupSettingsStore.getState().visualTweaks.threadQueryMarkdown && <ThreadQueryFormatSwitch />}
3537
<Toaster />
3638
<IncompatibleInterfaceLanguageNotice />
3739
<ReactQueryDevtools initialIsOpen={false} />

src/content-script/base.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,13 @@ body[data-scroll-locked].bg-transparent {
218218
display: none;
219219
}
220220

221-
body:has(#markdown-query-wrapper) {
221+
/* body:has(#markdown-query-wrapper) {
222222
223223
& h1.whitespace-pre-line.break-words,
224224
& div.whitespace-pre-line.break-words {
225225
display: none;
226226
}
227-
}
227+
} */
228228

229229
html.dark #markdown-query-wrapper {
230230
& pre code {

src/content-script/ui-tweaks.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,6 @@ function alternateMessageQuery({
283283
$query.append($newQueryWrapper);
284284

285285
$query.off('dblclick').on('dblclick', () => {
286-
if ($query.find('textarea').length) return;
287286
const $buttonBar = $messageBlock.find(
288287
'.mt-sm.flex.items-center.justify-between'
289288
);

0 commit comments

Comments
 (0)