diff --git a/services/user/Chainmail/ui/src/components/compose-dialog.tsx b/services/user/Chainmail/ui/src/components/compose-dialog.tsx index c6be19ef6..aea7bbf15 100644 --- a/services/user/Chainmail/ui/src/components/compose-dialog.tsx +++ b/services/user/Chainmail/ui/src/components/compose-dialog.tsx @@ -169,72 +169,78 @@ export const ComposeDialog = ({ > {trigger} { // This helps in not focusing on the trigger after closing the modal e.preventDefault(); }} > - - Compose message - - Send a message to another account on the network - - - -
- - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - - -
- - -
- -
-
- - - - - +
+
+ + + Compose message + + Send a message to another account on the + network + + + + ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + + +
+ + +
+ +
+
+ + + + + +
); diff --git a/services/user/Chainmail/ui/src/components/editor/link-widget.tsx b/services/user/Chainmail/ui/src/components/editor/link-widget.tsx index b15b4b21d..a8c101533 100644 --- a/services/user/Chainmail/ui/src/components/editor/link-widget.tsx +++ b/services/user/Chainmail/ui/src/components/editor/link-widget.tsx @@ -1,136 +1,64 @@ -import { commandsCtx } from "@milkdown/core"; -import { updateLinkCommand } from "@milkdown/preset-commonmark"; -import { Plugin } from "@milkdown/prose/state"; -import { DecorationSet } from "@milkdown/prose/view"; -import { useInstance } from "@milkdown/react"; -import { $prose } from "@milkdown/utils"; -import type { useWidgetViewFactory } from "@prosemirror-adapter/react"; -import { useWidgetViewContext } from "@prosemirror-adapter/react"; +import type { Ctx } from "@milkdown/ctx"; +import type { EditorView } from "@milkdown/prose/view"; +import type { EditorState } from "@milkdown/prose/state"; -export const LinkWidgetBefore = () => { - return <>[; -}; +import { editorViewCtx } from "@milkdown/core"; +import { linkSchema } from "@milkdown/preset-commonmark"; +import { + configureLinkTooltip, + linkTooltipAPI, + linkTooltipState, +} from "@milkdown/components/link-tooltip"; +import { TooltipProvider, tooltipFactory } from "@milkdown/plugin-tooltip"; -export const LinkWidgetAfter = () => { - const { spec } = useWidgetViewContext(); - const [loading, editor] = useInstance(); - const href = spec?.href ?? ""; - const title = spec?.title ?? ""; +import "../../styles/link-tooltip.css"; - return ( - <> - ] - - ( - { - <> - link: - { - if (loading) return; - editor().action((ctx) => { - const commands = ctx.get(commandsCtx); - commands.call(updateLinkCommand.key, { - href: e.target.value, - }); - }); - }} - className="rounded border-none bg-gray-50 px-2 py-0 ring-1 dark:bg-gray-900" - type="text" - defaultValue={href} - /> -   - title: - " - { - if (loading) return; - editor().action((ctx) => { - const commands = ctx.get(commandsCtx); - commands.call(updateLinkCommand.key, { - title: e.target.value, - }); - }); - }} - className="rounded border-none bg-gray-50 px-2 py-0 ring-1 dark:bg-gray-900" - type="text" - defaultValue={title} - /> - " - - } - ) - - - ); -}; +export { linkTooltipPlugin } from "@milkdown/components/link-tooltip"; -export const linkPlugin = ( - widgetViewFactory: ReturnType, -) => { - const before = widgetViewFactory({ - as: "span", - component: LinkWidgetBefore, - }); - const after = widgetViewFactory({ as: "span", component: LinkWidgetAfter }); - - return $prose( - () => - new Plugin({ - state: { - init() { - return DecorationSet.empty; - }, - apply(tr) { - const { selection } = tr; +export const insertLinkTooltip = tooltipFactory("CREATE_LINK"); - const { $from, $to } = selection; - const node = tr.doc.nodeAt(selection.from); +export function tooltipPluginView(ctx: Ctx) { + return (_view: EditorView) => { + const content = document.createElement("div"); + const provider = new TooltipProvider({ + content, + shouldShow: (view: EditorView) => { + const { selection, doc } = view.state; + const has = doc.rangeHasMark( + selection.from, + selection.to, + linkSchema.type(ctx), + ); + if (has || selection.empty) return false; - const mark = node?.marks.find( - (mark) => mark.type.name === "link", - ); + return true; + }, + }); - if (!mark) return DecorationSet.empty; + content.onmousedown = (e: MouseEvent) => { + e.preventDefault(); + const view = ctx.get(editorViewCtx); + const { selection } = view.state; + ctx.get(linkTooltipAPI.key).addLink(selection.from, selection.to); + provider.hide(); + }; - let markPos = { start: -1, end: -1 }; - tr.doc.nodesBetween( - $from.start(), - $to.end(), - (n, pos) => { - if (node === n) { - markPos = { - start: pos, - end: - pos + - Math.max(n.textContent.length, 1), - }; + return { + update: (updatedView: EditorView, prevState: EditorState) => { + if (ctx.get(linkTooltipState.key).mode === "edit") return; + provider.update(updatedView, prevState); + }, + destroy: () => { + provider.destroy(); + content.remove(); + }, + }; + }; +} - // stop recursing if result is found - return false; - } - return undefined; - }, - ); - - return DecorationSet.create(tr.doc, [ - before(markPos.start), - after(markPos.end, { - href: mark.attrs.href, - title: mark.attrs.title, - }), - ]); - }, - }, - props: { - decorations(state) { - return this.getState(state); - }, - }, - }), - ); -}; +export function linkTooltipConfig(ctx: Ctx) { + ctx.set(insertLinkTooltip.key, { + view: tooltipPluginView(ctx), + }); + configureLinkTooltip(ctx); +} diff --git a/services/user/Chainmail/ui/src/components/markdown-editor.tsx b/services/user/Chainmail/ui/src/components/markdown-editor.tsx index c0c079de2..7dada5b5c 100644 --- a/services/user/Chainmail/ui/src/components/markdown-editor.tsx +++ b/services/user/Chainmail/ui/src/components/markdown-editor.tsx @@ -1,6 +1,3 @@ -import type { MilkdownPlugin } from "@milkdown/ctx"; - -import { useMemo } from "react"; import { atom, useSetAtom } from "jotai"; import { defaultValueCtx, @@ -9,21 +6,17 @@ import { rootCtx, schemaCtx, } from "@milkdown/core"; -import { $view } from "@milkdown/utils"; +// import { $view } from "@milkdown/utils"; import { nord } from "@milkdown/theme-nord"; import { Milkdown, useEditor } from "@milkdown/react"; import { commonmark } from "@milkdown/preset-commonmark"; import { gfm } from "@milkdown/preset-gfm"; import { listItemBlockComponent } from "@milkdown/components/list-item-block"; -import { - configureLinkTooltip, - linkTooltipPlugin, -} from "@milkdown/components/link-tooltip"; import { listener, listenerCtx } from "@milkdown/plugin-listener"; import { history } from "@milkdown/plugin-history"; // import { math, mathBlockSchema } from "@milkdown/plugin-math"; // import { diagram, diagramSchema } from "@milkdown/plugin-diagram"; -import { useNodeViewFactory } from "@prosemirror-adapter/react"; +// import { useNodeViewFactory } from "@prosemirror-adapter/react"; import { Node, Schema } from "@milkdown/prose/model"; import { @@ -33,6 +26,11 @@ import { // import { MathBlock, MermaidDiagram } from "./editor"; // import { MathBlock } from "./editor"; +import { + insertLinkTooltip, + linkTooltipConfig, + linkTooltipPlugin, +} from "./editor/link-widget"; import "@milkdown/theme-nord/style.css"; // import "katex/dist/katex.min.css"; @@ -56,7 +54,7 @@ export const MarkdownEditor = ({ updateMarkdown, readOnly = false, }: EditorProps) => { - const nodeViewFactory = useNodeViewFactory(); + // const nodeViewFactory = useNodeViewFactory(); const setSelection = useSetAtom(editorSelectionAtom); // const mathPlugins: MilkdownPlugin[] = useMemo(() => { @@ -89,8 +87,8 @@ export const MarkdownEditor = ({ .config((ctx) => { ctx.set(rootCtx, root); ctx.set(defaultValueCtx, initialValue); - configureLinkTooltip(ctx); }) + .config(linkTooltipConfig) .config((ctx) => { if (readOnly) return; const listener = ctx.get(listenerCtx); @@ -120,7 +118,8 @@ export const MarkdownEditor = ({ .use(listItemBlockComponent) // .use(mathPlugins) // .use(diagramPlugins) - .use(linkTooltipPlugin), + .use(linkTooltipPlugin) + .use(insertLinkTooltip), ); return ; diff --git a/services/user/Chainmail/ui/src/hooks/use-user.ts b/services/user/Chainmail/ui/src/hooks/use-user.ts index b0023cd55..a63bdaeda 100644 --- a/services/user/Chainmail/ui/src/hooks/use-user.ts +++ b/services/user/Chainmail/ui/src/hooks/use-user.ts @@ -48,7 +48,13 @@ export function useUser() { }; useEffect(() => { - getConnectedAccounts(); + const logIn = async () => { + await logInAs("alice"); + await logInAs("bob"); + getConnectedAccounts(); + }; + if (availableAccounts.length) return; + logIn(); }, []); return { diff --git a/services/user/Chainmail/ui/src/routes/home.tsx b/services/user/Chainmail/ui/src/routes/home.tsx index 83ff93b7f..a3bbcaa87 100644 --- a/services/user/Chainmail/ui/src/routes/home.tsx +++ b/services/user/Chainmail/ui/src/routes/home.tsx @@ -37,13 +37,9 @@ export function Home() {

Inbox

- - - } - /> - + } + />
diff --git a/services/user/Chainmail/ui/src/styles/editor.css b/services/user/Chainmail/ui/src/styles/editor.css index 8fc586848..462f5d51a 100644 --- a/services/user/Chainmail/ui/src/styles/editor.css +++ b/services/user/Chainmail/ui/src/styles/editor.css @@ -10,6 +10,10 @@ @apply mx-auto h-full max-w-none dark:text-foreground; } +.prose a { + @apply dark:text-foreground; +} + /* list-item-block */ .prose :where(li):not(:where([class~="not-prose"] *)) { margin-top: 0.5em; @@ -68,89 +72,3 @@ milkdown-list-item-block .label-wrapper { align-items: center; color: darkcyan; } - -milkdown-link-preview { - & > .link-preview { - @apply rounded-md border bg-white; - height: 42px; - display: flex; - justify-content: center; - padding: 8px; - gap: 8px; - cursor: pointer; - - & > .link-display:hover:before { - display: block; - } - - & > .link-display:before { - content: "click to copy link"; - position: absolute; - transform: translate(50%, -130%); - padding: 6px 10px; - font-size: 12px; - border-radius: 10px; - background: #000; - color: #fff; - text-align: center; - - display: none; - } - - & > .link-icon { - & > svg { - width: 14px; - height: 14px; - } - padding: 5px; - } - - & > .link-display { - width: 240px; - line-height: 24px; - overflow: hidden; - text-overflow: ellipsis; - font-size: 14px; - &:hover { - text-decoration: underline; - } - } - - & > .button { - & > svg { - width: 14px; - height: 14px; - } - padding: 5px; - @apply rounded-md; - &:hover { - @apply bg-accent; - } - } - } -} - -milkdown-link-edit { - & > .link-edit { - @apply flex justify-center gap-2 rounded-md border bg-white px-3 py-2; - height: 42px; - - & > .input-area { - outline: none; - background: transparent; - width: 200px; - font-size: 14px; - } - - & > .button { - @apply flex cursor-pointer items-center rounded-md px-1.5; - font-size: 12px; - &:hover { - @apply bg-accent; - } - &.hidden { - visibility: hidden; - } - } - } -} diff --git a/services/user/Chainmail/ui/src/styles/link-tooltip.css b/services/user/Chainmail/ui/src/styles/link-tooltip.css new file mode 100644 index 000000000..1eb21df00 --- /dev/null +++ b/services/user/Chainmail/ui/src/styles/link-tooltip.css @@ -0,0 +1,99 @@ +milkdown-link-preview { + position: absolute; + &[data-show="false"] { + display: none; + } + + @apply dark:text-background; + + & > .link-preview { + @apply absolute -right-40 -top-12 z-20 rounded-md border bg-white; + height: 42px; + display: flex; + justify-content: center; + padding: 8px; + gap: 8px; + cursor: pointer; + + & > .link-display:hover:before { + display: block; + } + + & > .link-display:before { + content: "click to copy link"; + position: absolute; + transform: translate(50%, -130%); + padding: 6px 10px; + font-size: 12px; + border-radius: 10px; + background: #000; + color: #fff; + text-align: center; + + display: none; + } + + & > .link-icon { + @apply px-1; + & > svg { + width: 14px; + height: 14px; + } + } + + & > .link-display { + width: 240px; + line-height: 24px; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px; + + &:hover { + text-decoration: underline; + } + } + + & > .button { + & > svg { + width: 14px; + height: 14px; + } + @apply rounded-md px-1; + &:hover { + @apply bg-accent dark:text-foreground; + } + } + } +} + +milkdown-link-edit { + position: absolute; + &[data-show="false"] { + display: none; + } + + @apply dark:text-background; + + & > .link-edit { + @apply absolute -right-40 -top-12 z-20 flex justify-center gap-2 rounded-md border bg-white px-3 py-2; + height: 42px; + + & > .input-area { + outline: none; + background: transparent; + width: 200px; + font-size: 14px; + } + + & > .button { + @apply flex w-max cursor-pointer items-center rounded-md px-1.5; + font-size: 12px; + &:hover { + @apply bg-accent dark:text-foreground; + } + &.hidden { + visibility: hidden; + } + } + } +}