From 4aefb4f7a586a11defe39857f51d9de0328c9405 Mon Sep 17 00:00:00 2001 From: coolsyntax Date: Wed, 17 Apr 2024 15:00:05 +0530 Subject: [PATCH 1/2] Created userTypingTooltipField in cursor.ts to display user name while typing --- jupyter_collaboration/_version.py | 4 ++ packages/collaboration/src/cursors.ts | 96 ++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 jupyter_collaboration/_version.py diff --git a/jupyter_collaboration/_version.py b/jupyter_collaboration/_version.py new file mode 100644 index 00000000..4b14c7ff --- /dev/null +++ b/jupyter_collaboration/_version.py @@ -0,0 +1,4 @@ +# This file is auto-generated by Hatchling. As such, do not: +# - modify +# - track in version control e.g. be sure to add to .gitignore +__version__ = VERSION = '2.0.11' diff --git a/packages/collaboration/src/cursors.ts b/packages/collaboration/src/cursors.ts index aa850524..f50dea8c 100644 --- a/packages/collaboration/src/cursors.ts +++ b/packages/collaboration/src/cursors.ts @@ -4,8 +4,10 @@ import { Annotation, EditorSelection, + EditorState, Extension, - Facet + Facet, + StateField, } from '@codemirror/state'; import { EditorView, @@ -15,7 +17,9 @@ import { RectangleMarker, tooltips, ViewPlugin, - ViewUpdate + ViewUpdate, + Tooltip, + showTooltip, } from '@codemirror/view'; import { User } from '@jupyterlab/services'; import { JSONExt } from '@lumino/coreutils'; @@ -276,6 +280,92 @@ const userHover = hoverTooltip( } ); +/** + * Extension displaying display name on the cursor currently typing + */ +// const userTypingTooltipField = StateField.define({ +// create: (state) => getUserTypingTooltips(state), +// update: (tooltips, tr) => { +// if (!tr.docChanged && !tr.selection) return tooltips; +// return getUserTypingTooltips(tr.state); +// }, +// provide: (f) => showTooltip.computeN([f], (state) => state.field(f)), +// }); + +// function getUserTypingTooltips(state: EditorState): readonly Tooltip[] { +// const { awareness, ytext } = state.facet(editorAwarenessFacet); +// const ydoc = ytext.doc!; + +// for (const [clientID, state] of awareness.getStates()) { +// if (clientID === awareness.doc.clientID) { +// continue; +// } + +// for (const cursor of state.cursors ?? []) { +// if (!cursor?.head) { +// continue; +// } +// const head = createAbsolutePositionFromRelativePosition( +// cursor.head, +// ydoc +// ); +// if (head?.type !== ytext) { +// continue; +// } +// // Use some margin around the cursor to display the user. +// //if (head.index - 3 <= pos && pos <= head.index + 3) { +// return { +// //pos: head.index, +// above: true, +// create: () => { +// const dom = document.createElement('div'); +// dom.classList.add('jp-remote-userInfo'); +// dom.style.backgroundColor = state.user?.color ?? 'darkgrey'; +// dom.textContent = +// (state as IAwarenessState).user?.display_name ?? 'Anonymous'; +// return { dom }; +// } +// }; +// //} +// } +// } + +// return []; +// } +const collaboratorNameTooltipField = StateField.define({ + create: (state) => getCollaboratorNameTooltips(state), + update: (tooltips, tr) => { + if (!tr.docChanged && !tr.selection) return tooltips; + return getCollaboratorNameTooltips(tr.state); + }, + provide: (f) => showTooltip.computeN([f], (state) => state.field(f)), +}); + +function getCollaboratorNameTooltips(state: EditorState): readonly Tooltip[] { + const { awareness } = state.facet(editorAwarenessFacet); + const tooltips: Tooltip[] = []; + + for (const [clientID, clientState] of awareness.getStates()) { + if (clientID === awareness.doc.clientID) continue; + + const cursor = clientState.cursors?.[0]; + if (cursor?.head) { + tooltips.push({ + pos: cursor.head.index, + above: true, + create: () => { + const dom = document.createElement("div"); + dom.classList.add("jp-remote-userInfo"); + dom.style.backgroundColor = clientState.user?.color ?? "darkgrey"; + dom.textContent = clientState.user?.display_name ?? "Anonymous"; + return { dom }; + }, + }); + } + } + + return tooltips; +} /** * Extension defining a new editor layer storing the remote selections */ @@ -428,6 +518,8 @@ const showCollaborators = ViewPlugin.fromClass( remoteCursorsLayer, remoteSelectionLayer, userHover, + //userTypingTooltipField, + collaboratorNameTooltipField, // As we use relative positioning of widget, the tooltip must be positioned absolutely // And we attach the tooltip to the body to avoid overflow rules tooltips({ position: 'absolute', parent: document.body }) From 87300975acdf7da51dbe6e56c8fd9023625cabfb Mon Sep 17 00:00:00 2001 From: coolsyntax Date: Wed, 17 Apr 2024 15:18:56 +0530 Subject: [PATCH 2/2] Created userTypingTooltipField to display name on the cursor currently typing --- packages/collaboration/src/cursors.ts | 98 +++++++-------------------- 1 file changed, 23 insertions(+), 75 deletions(-) diff --git a/packages/collaboration/src/cursors.ts b/packages/collaboration/src/cursors.ts index f50dea8c..71294234 100644 --- a/packages/collaboration/src/cursors.ts +++ b/packages/collaboration/src/cursors.ts @@ -20,6 +20,8 @@ import { ViewUpdate, Tooltip, showTooltip, + Decoration, + DecorationSet, } from '@codemirror/view'; import { User } from '@jupyterlab/services'; import { JSONExt } from '@lumino/coreutils'; @@ -283,89 +285,36 @@ const userHover = hoverTooltip( /** * Extension displaying display name on the cursor currently typing */ -// const userTypingTooltipField = StateField.define({ -// create: (state) => getUserTypingTooltips(state), -// update: (tooltips, tr) => { -// if (!tr.docChanged && !tr.selection) return tooltips; -// return getUserTypingTooltips(tr.state); -// }, -// provide: (f) => showTooltip.computeN([f], (state) => state.field(f)), -// }); - -// function getUserTypingTooltips(state: EditorState): readonly Tooltip[] { -// const { awareness, ytext } = state.facet(editorAwarenessFacet); -// const ydoc = ytext.doc!; - -// for (const [clientID, state] of awareness.getStates()) { -// if (clientID === awareness.doc.clientID) { -// continue; -// } - -// for (const cursor of state.cursors ?? []) { -// if (!cursor?.head) { -// continue; -// } -// const head = createAbsolutePositionFromRelativePosition( -// cursor.head, -// ydoc -// ); -// if (head?.type !== ytext) { -// continue; -// } -// // Use some margin around the cursor to display the user. -// //if (head.index - 3 <= pos && pos <= head.index + 3) { -// return { -// //pos: head.index, -// above: true, -// create: () => { -// const dom = document.createElement('div'); -// dom.classList.add('jp-remote-userInfo'); -// dom.style.backgroundColor = state.user?.color ?? 'darkgrey'; -// dom.textContent = -// (state as IAwarenessState).user?.display_name ?? 'Anonymous'; -// return { dom }; -// } -// }; -// //} -// } -// } - -// return []; -// } -const collaboratorNameTooltipField = StateField.define({ - create: (state) => getCollaboratorNameTooltips(state), +const userTypingTooltipField = StateField.define({ + create: (state) => getUserTypingTooltips(state), update: (tooltips, tr) => { if (!tr.docChanged && !tr.selection) return tooltips; - return getCollaboratorNameTooltips(tr.state); + return getUserTypingTooltips(tr.state); }, provide: (f) => showTooltip.computeN([f], (state) => state.field(f)), }); -function getCollaboratorNameTooltips(state: EditorState): readonly Tooltip[] { +function getUserTypingTooltips(state: EditorState): readonly Tooltip[] { const { awareness } = state.facet(editorAwarenessFacet); - const tooltips: Tooltip[] = []; - - for (const [clientID, clientState] of awareness.getStates()) { - if (clientID === awareness.doc.clientID) continue; - - const cursor = clientState.cursors?.[0]; - if (cursor?.head) { - tooltips.push({ - pos: cursor.head.index, - above: true, - create: () => { - const dom = document.createElement("div"); - dom.classList.add("jp-remote-userInfo"); - dom.style.backgroundColor = clientState.user?.color ?? "darkgrey"; - dom.textContent = clientState.user?.display_name ?? "Anonymous"; - return { dom }; - }, - }); - } + const localAwarenessState = awareness.getLocalState() as IAwarenessState | null; + + if (localAwarenessState && localAwarenessState.state.cursors?.length > 0) { + return localAwarenessState.state.cursors.map((cursor: { head: { index: any; }; }) => ({ + pos: cursor.head.index, + above: true, + create: () => { + const dom = document.createElement("div"); + dom.classList.add("jp-remote-userInfo"); + dom.style.backgroundColor = localAwarenessState.user?.color ?? "darkgrey"; + dom.textContent = localAwarenessState.user?.display_name ?? "Anonymous"; + return { dom }; + }, + })); } - return tooltips; + return []; } + /** * Extension defining a new editor layer storing the remote selections */ @@ -518,8 +467,7 @@ const showCollaborators = ViewPlugin.fromClass( remoteCursorsLayer, remoteSelectionLayer, userHover, - //userTypingTooltipField, - collaboratorNameTooltipField, + userTypingTooltipField, // As we use relative positioning of widget, the tooltip must be positioned absolutely // And we attach the tooltip to the body to avoid overflow rules tooltips({ position: 'absolute', parent: document.body })