Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop/src/components/editor-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ async function generateTitleDirect(
if (!session?.title && sessions[targetSessionId]?.getState) {
const cleanedTitle = text.replace(/^["']|["']$/g, "").trim();
sessions[targetSessionId].getState().updateTitle(cleanedTitle);
}

if (sessions[targetSessionId]?.getState) {
try {
const suggestedTags = await autoTagGeneration(targetSessionId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@ export function ChatInput(
}
}, [chatInputRef]);

useEffect(() => {
const editor = editorRef.current?.editor;
if (editor) {
// override TipTap's Enter behavior completely
editor.setOptions({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly overriding a third-party library's core behavior (TipTap editor's handleKeyDown via editorProps) can lead to brittle and unmaintainable code. This approach tightly couples the component to TipTap's internal implementation details, making future upgrades and bug fixes challenging. It circumvents TipTap's intended extension mechanisms and can introduce unexpected side effects or breakages when the library updates. A more robust solution would be to use TipTap's official extension API or event listeners, if available, for custom key handling.

Prompt for AI agents
Address the following comment on apps/desktop/src/components/right-panel/components/chat/chat-input.tsx at line 210:

<comment>Directly overriding a third-party library&#39;s core behavior (`TipTap` editor&#39;s `handleKeyDown` via `editorProps`) can lead to brittle and unmaintainable code. This approach tightly couples the component to `TipTap`&#39;s internal implementation details, making future upgrades and bug fixes challenging. It circumvents `TipTap`&#39;s intended extension mechanisms and can introduce unexpected side effects or breakages when the library updates. A more robust solution would be to use TipTap&#39;s official extension API or event listeners, if available, for custom key handling.</comment>

<file context>
@@ -203,6 +203,32 @@ export function ChatInput(
     }
   }, [chatInputRef]);
 
+  useEffect(() =&gt; {
+    const editor = editorRef.current?.editor;
+    if (editor) {
+      // override TipTap&#39;s Enter behavior completely
+      editor.setOptions({
+        editorProps: {
</file context>

editorProps: {
...editor.options.editorProps,
handleKeyDown: (view, event) => {
if (event.key === "Enter" && !event.shiftKey) {
const isEmpty = view.state.doc.textContent.trim() === "";
if (isEmpty) {
return true;
}
if (inputValue.trim()) {
event.preventDefault();
handleSubmit();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop propagation in the TipTap handleKeyDown handler before submitting to avoid duplicate Enter handling with the DOM keydown listener (which also triggers submit)

Prompt for AI agents
Address the following comment on apps/desktop/src/components/right-panel/components/chat/chat-input.tsx at line 221:

<comment>Stop propagation in the TipTap handleKeyDown handler before submitting to avoid duplicate Enter handling with the DOM keydown listener (which also triggers submit)</comment>

<file context>
@@ -203,6 +203,32 @@ export function ChatInput(
     }
   }, [chatInputRef]);
 
+  useEffect(() =&gt; {
+    const editor = editorRef.current?.editor;
+    if (editor) {
+      // override TipTap&#39;s Enter behavior completely
+      editor.setOptions({
+        editorProps: {
</file context>
Suggested change
handleSubmit();
event.stopPropagation(); handleSubmit();

return true;
}
}
return false;
},
},
});
}
}, [editorRef.current?.editor, inputValue, handleSubmit]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid re-applying editor.setOptions on every keystroke; remove inputValue from the effect dependencies or use a ref to access the latest value inside the handler

Prompt for AI agents
Address the following comment on apps/desktop/src/components/right-panel/components/chat/chat-input.tsx at line 230:

<comment>Avoid re-applying editor.setOptions on every keystroke; remove inputValue from the effect dependencies or use a ref to access the latest value inside the handler</comment>

<file context>
@@ -203,6 +203,32 @@ export function ChatInput(
     }
   }, [chatInputRef]);
 
+  useEffect(() =&gt; {
+    const editor = editorRef.current?.editor;
+    if (editor) {
+      // override TipTap&#39;s Enter behavior completely
+      editor.setOptions({
+        editorProps: {
</file context>


Comment on lines +206 to +231
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Enter is handled twice (ProseMirror handler + DOM listener) — risk of double submission

You now override TipTap’s handleKeyDown and still attach a DOM keydown listener below. Both intercept Enter and call handleSubmit, causing duplicate sends. Also, the PM handler doesn’t stop propagation; and the effect resets editor options on every keystroke via inputValue dependency.

  • Fix: stop propagation in the PM handler and make the DOM handler ignore defaultPrevented.
  • Optional: avoid resetting editor options on every input change by reading inputValue from a ref.

Apply this diff to the PM handler to stop propagation:

           if (isEmpty) {
-            return true;
+            event.preventDefault();
+            event.stopPropagation();
+            return true;
           }
           if (inputValue.trim()) {
             event.preventDefault();
+            event.stopPropagation();
             handleSubmit();
             return true;
           }

And update the DOM keydown handler to skip when already handled (see the next comment’s diff).

Optional (outside the selected lines): use a ref to avoid re-calling setOptions on every inputValue change.

// outside: before effects
const inputValueRef = useRef(inputValue);
useEffect(() => { inputValueRef.current = inputValue; }, [inputValue]);

// inside PM handler, replace inputValue.trim() with:
if (inputValueRef.current.trim()) { ... }

// and make the setOptions effect depend only on editorRef.current?.editor
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/components/chat/chat-input.tsx around
lines 206-231, the ProseMirror (TipTap) handleKeyDown currently triggers
handleSubmit but doesn't stop propagation and the effect re-applies editor
options on every input change; update the PM handler to call
event.preventDefault(); event.stopPropagation(); and return true when handling
Enter so it won't bubble, replace direct inputValue usage with a ref (e.g.,
inputValueRef.current) so setOptions doesn't need to run on every keystroke, and
change the useEffect dependency to only depend on editorRef.current?.editor;
additionally ensure the separate DOM keydown listener (elsewhere) checks
event.defaultPrevented and skips if true so submissions aren't duplicated.

useEffect(() => {
const editor = editorRef.current?.editor;
if (editor) {
Expand Down Expand Up @@ -264,7 +290,7 @@ export function ChatInput(
.chat-editor .tiptap-normal {
padding: 12px 40px 12px 12px !important;
min-height: 50px !important;
max-height: 80px!important;
max-height: 90px !important;
font-size: 14px !important;
line-height: 1.5 !important;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function TranscriptPanelButton() {
</TooltipTrigger>
<TooltipContent>
<p>
<Trans>Toggle transcriptpanel</Trans> <Shortcut macDisplay="⌘R" windowsDisplay="Ctrl+R" />
<Trans>Toggle transcript panel</Trans> <Shortcut macDisplay="⌘R" windowsDisplay="Ctrl+R" />
</p>
</TooltipContent>
</Tooltip>
Expand Down
8 changes: 6 additions & 2 deletions apps/desktop/src/locales/en/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -1545,8 +1545,12 @@ msgid "Toggle left sidebar"
msgstr "Toggle left sidebar"

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
msgid "Toggle transcriptpanel"
msgstr "Toggle transcriptpanel"
msgid "Toggle transcript panel"
msgstr "Toggle transcript panel"

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
#~ msgid "Toggle transcriptpanel"
#~ msgstr "Toggle transcriptpanel"

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
#~ msgid "Toggle widget panel"
Expand Down
6 changes: 5 additions & 1 deletion apps/desktop/src/locales/ko/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -1545,9 +1545,13 @@ msgid "Toggle left sidebar"
msgstr ""

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
msgid "Toggle transcriptpanel"
msgid "Toggle transcript panel"
msgstr ""

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
#~ msgid "Toggle transcriptpanel"
#~ msgstr ""

#: src/components/toolbar/buttons/transcript-panel-button.tsx:36
#~ msgid "Toggle widget panel"
#~ msgstr ""
Expand Down
66 changes: 65 additions & 1 deletion apps/desktop/src/routes/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { commands as localSttCommands } from "@hypr/plugin-local-stt";
import { createFileRoute, Outlet, useLocation, useRouter } from "@tanstack/react-router";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { watch } from "@tauri-apps/plugin-fs";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";

import { IndividualizationModal } from "@/components/individualization-modal";
import LeftSidebar from "@/components/left-sidebar";
Expand Down Expand Up @@ -38,6 +38,69 @@ export const Route = createFileRoute("/app")({
},
});

// still experimental
function ResponsivePanelsManager() {
const { isExpanded: leftExpanded, setIsExpanded: setLeftExpanded } = useLeftSidebar();
const { isExpanded: rightExpanded, setIsExpanded: setRightExpanded } = useRightPanel();

const [wasAutoCollapsed, setWasAutoCollapsed] = useState(false);

const originalStates = useRef<{ left: boolean; right: boolean } | null>(null);
const userOverrodeLeft = useRef(false);
const userOverrodeRight = useRef(false);

// trackmanual changes during auto-collapse
useEffect(() => {
if (wasAutoCollapsed && originalStates.current) {
if (leftExpanded !== false) {
userOverrodeLeft.current = true;
}
if (rightExpanded !== false) {
userOverrodeRight.current = true;
}
}
}, [leftExpanded, rightExpanded, wasAutoCollapsed]);

useEffect(() => {
const handleResize = () => {
const BREAKPOINT = 670;
const currentWidth = window.innerWidth;

if (currentWidth < BREAKPOINT) {
if (!wasAutoCollapsed) {
originalStates.current = { left: leftExpanded, right: rightExpanded };
userOverrodeLeft.current = false;
userOverrodeRight.current = false;

setLeftExpanded(false);
setRightExpanded(false);
setWasAutoCollapsed(true);
}
} else {
if (wasAutoCollapsed && originalStates.current) {
if (!userOverrodeLeft.current) {
setLeftExpanded(originalStates.current.left);
}
if (!userOverrodeRight.current) {
setRightExpanded(originalStates.current.right);
}

setWasAutoCollapsed(false);
originalStates.current = null;
userOverrodeLeft.current = false;
userOverrodeRight.current = false;
}
}
};

handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [leftExpanded, rightExpanded, wasAutoCollapsed, setLeftExpanded, setRightExpanded]);

return null;
}

function Component() {
const router = useRouter();
const location = useLocation();
Expand Down Expand Up @@ -88,6 +151,7 @@ function Component() {
</ResizablePanelGroup>
</div>
</div>
<ResponsivePanelsManager />
<WelcomeModal
isOpen={shouldShowWelcomeModal}
onClose={() => {
Expand Down
28 changes: 28 additions & 0 deletions packages/tiptap/src/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,34 @@ turndown.addRule("p", {
},
});

turndown.addRule("taskList", {
filter: function(node) {
return node.nodeName === "UL" && node.getAttribute("data-type") === "taskList";
},
replacement: function(content) {
return content;
},
});

turndown.addRule("taskItem", {
filter: function(node) {
if (node.nodeName !== "LI" || !node.parentNode) {
return false;
}
const parent = node.parentNode as HTMLElement;
return parent.nodeName === "UL" && parent.getAttribute("data-type") === "taskList";
},
replacement: function(content, node) {
const checkbox = node.querySelector("input[type=\"checkbox\"]") as HTMLInputElement;
const isChecked = checkbox ? checkbox.checked : false;
const checkboxSymbol = isChecked ? "[x]" : "[ ]";

const cleanContent = content.replace(/^\s*\[[\sxX]\]\s*/, "").trim();

return `- ${checkboxSymbol} ${cleanContent}\n`;
},
});

export function html2md(html: string) {
return turndown.turndown(html);
}
Loading