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
19 changes: 11 additions & 8 deletions apps/desktop/src/components/right-panel/views/transcript-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,16 +377,19 @@ const MemoizedSpeakerSelector = memo(({
};

return (
<div className="mt-2 sticky top-0 z-10 bg-neutral-50">
<div className="mt-4 sticky top-0 z-10 bg-neutral-50">
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger
onMouseDown={(e) => {
e.preventDefault();
}}
>
<span className="underline py-1 font-semibold">
<PopoverTrigger asChild>
<Button
variant="outline"
size="sm"
className="h-auto p-1 font-semibold text-neutral-700 hover:text-neutral-900 -ml-1"
onMouseDown={(e) => {
e.preventDefault();
}}
>
{getDisplayName(human)}
</span>
</Button>
</PopoverTrigger>
<PopoverContent align="start" side="bottom">
<div className="space-y-4">
Expand Down
82 changes: 81 additions & 1 deletion packages/tiptap/src/transcript/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const SpeakerSplit = Extension.create({
key: new PluginKey("hypr-speaker-split"),
props: {
handleKeyDown(view, event) {
// Handle Enter key for splitting speakers
if (checkKey("Enter")(event)) {
const { state, dispatch } = view;
const { selection } = state;
Expand Down Expand Up @@ -65,6 +66,84 @@ export const SpeakerSplit = Extension.create({
return true;
}

// Handle Up/Down arrow keys for smart section navigation
if (checkKey("ArrowUp")(event) || checkKey("ArrowDown")(event)) {
const { state } = view;
const { selection } = state;
const { doc } = state;
const isUp = event.key === "ArrowUp";

// Find all speaker sections
const speakerSections: { pos: number; node: any; startPos: number; endPos: number }[] = [];
doc.descendants((node, pos) => {
if (node.type.name === SPEAKER_NODE_NAME) {
speakerSections.push({
pos,
node,
startPos: pos + 1, // Content starts after the node opening
endPos: pos + node.nodeSize - 1, // Content ends before the node closing
});
}
return false; // Don't descend into speaker nodes
});

if (speakerSections.length < 2) {
return false; // Need at least 2 sections to navigate
}

// Find current speaker section
const currentPos = selection.from;
let currentSection: typeof speakerSections[0] | null = null;
let currentSectionIndex = -1;

for (let i = 0; i < speakerSections.length; i++) {
const section = speakerSections[i];
if (currentPos >= section.startPos && currentPos <= section.endPos) {
currentSection = section;
currentSectionIndex = i;
break;
}
}

if (!currentSection || currentSectionIndex === -1) {
return false; // Not within a speaker section
}

// Check if we're at the edge of the current section
const { $from } = selection;
const isAtEdge = isUp
? currentPos === currentSection.startPos || $from.parentOffset === 0
: currentPos === currentSection.endPos || $from.parentOffset === $from.parent.content.size;

// Only navigate between sections if we're at the edge
if (!isAtEdge) {
return false; // Let default line navigation handle this
}

// We're at the edge, so navigate to adjacent section
let targetSectionIndex: number;
if (isUp) {
targetSectionIndex = currentSectionIndex > 0 ? currentSectionIndex - 1 : speakerSections.length - 1;
} else {
targetSectionIndex = currentSectionIndex < speakerSections.length - 1 ? currentSectionIndex + 1 : 0;
}

const targetSection = speakerSections[targetSectionIndex];
let targetPos: number;

if (isUp) {
// When going up, go to the end of the previous section
targetPos = targetSection.endPos;
} else {
// When going down, go to the beginning of the next section
targetPos = targetSection.startPos;
}

const newSelection = TextSelection.create(doc, targetPos);
view.dispatch(state.tr.setSelection(newSelection).scrollIntoView());
return true;
}

return false;
},
},
Expand All @@ -77,5 +156,6 @@ const checkKey = (key: string) => (e: KeyboardEvent) => {
return e.key === key
&& !e.ctrlKey
&& !e.metaKey
&& !e.altKey;
&& !e.altKey
&& !e.shiftKey; // Also ignore shift key for cleaner navigation
};
Loading