@meowdown/react 0.33.1 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -226,7 +226,8 @@ interface EditorProps {
226
226
  selectionMenuAffordance?: boolean;
227
227
  /**
228
228
  * Extra controls rendered in the pending-replacement preview footer, next to
229
- * the built-in Accept and Discard buttons (e.g. a retry button).
229
+ * the built-in accept ("Replace selection" / "Insert below", per the staged
230
+ * mode) and Discard buttons (e.g. a retry button).
230
231
  */
231
232
  pendingReplacementActions?: ReactNode;
232
233
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { clsx } from "clsx/lite";
2
2
  import { Fragment, createElement, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
3
- import { codeBlockLanguages, defaultResolveImageUrl, defineBulletAfterHeading, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLPaste, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineMarkMode, defineMarkdownCopy, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSpellCheckPlugin, defineTagClickHandler, defineWikilinkClickHandler, defineWikilinkTrigger, docToMarkdown, getCodeTokens, getMarkBuilders, getPendingReplacement, getSelectedText, getVirtualElementFromRange, inlineTextToMarkChunks, isSelectionInTableCell, listenForTweetHeight, markdownToDoc, matchEmbed } from "@meowdown/core";
3
+ import { codeBlockLanguages, defaultResolveImageUrl, defineBulletAfterHeading, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLPaste, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineMarkdownCopy, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSpellCheckPlugin, defineTagClickHandler, defineWikilinkClickHandler, defineWikilinkTrigger, docToMarkdown, getCodeTokens, getMarkBuilders, getPendingReplacement, getSelectedText, getTableColumnAlign, getVirtualElementFromRange, inlineTextToMarkChunks, isSelectionInTableCell, listenForTweetHeight, markdownToDoc, matchEmbed } from "@meowdown/core";
4
4
  import { clamp } from "@ocavue/utils";
5
5
  import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineUpdateHandler, isTextSelection, union } from "@prosekit/core";
6
6
  import { Selection, TextSelection } from "@prosekit/pm/state";
@@ -217,9 +217,10 @@ function DropIndicator$1() {
217
217
  //#endregion
218
218
  //#region src/components/editor-extensions.tsx
219
219
  function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick, onTagClick, onExitBoundary, resolveImageUrl, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, bulletAfterHeading, placeholder, readOnly, wikilinkEnabled, spellCheck }) {
220
- useExtension$1(useMemo(() => {
221
- return defineMarkMode(markMode);
222
- }, [markMode]));
220
+ const editor = useEditor$1();
221
+ useEffect(() => {
222
+ editor.commands.setMarkMode(markMode);
223
+ }, [editor, markMode]);
223
224
  useExtension$1(useMemo(() => {
224
225
  return readOnly ? defineReadonly() : null;
225
226
  }, [readOnly]));
@@ -577,10 +578,27 @@ var pending_replacement_preview_module_default = {
577
578
  //#endregion
578
579
  //#region src/components/pending-replacement-preview.tsx
579
580
  /**
581
+ * Vertical room (px) the popover needs under its anchor: the text area's
582
+ * 14rem max-height plus the footer, borders, and the anchor offset.
583
+ */
584
+ const PREVIEW_CLEARANCE = 320;
585
+ /** Minimum gap (px) kept above the anchor line when scrolling to make room. */
586
+ const SCROLL_TOP_MARGIN = 16;
587
+ /** The nearest ancestor that can scroll vertically, or the page scroller. */
588
+ function closestScrollable(element) {
589
+ for (let node = element.parentElement; node; node = node.parentElement) {
590
+ const { overflowY } = getComputedStyle(node);
591
+ if (/(auto|scroll|overlay)/.test(overflowY) && node.scrollHeight > node.clientHeight) return node;
592
+ }
593
+ return document.scrollingElement;
594
+ }
595
+ /**
580
596
  * The preview for a staged (pending) replacement: a popover anchored to the
581
- * source range showing the accumulated text, with Accept and Discard controls
582
- * plus a host-provided `actions` slot. Dismissing the popover (Escape or an
583
- * outside press) discards the stage; the document is only touched on accept.
597
+ * end of the source range showing the accumulated text, with a Discard control
598
+ * and an accept control labeled by what accepting does ("Replace selection" or
599
+ * "Insert below", per the staged mode), plus a host-provided `actions` slot.
600
+ * Dismissing the popover (Escape or an outside press) discards the stage; the
601
+ * document is only touched on accept.
584
602
  */
585
603
  function PendingReplacementPreview({ actions, onResolve }) {
586
604
  const editor = useEditor$1();
@@ -594,19 +612,35 @@ function PendingReplacementPreview({ actions, onResolve }) {
594
612
  }
595
613
  });
596
614
  }, [onResolve]));
597
- const from = pending?.from;
598
615
  const to = pending?.to;
599
616
  const anchor = useMemo(() => {
600
- if (from == null || to == null) return;
617
+ if (to == null) return;
601
618
  return getVirtualElementFromRange(editor.view, {
602
- from,
619
+ from: to,
603
620
  to
604
621
  });
605
- }, [
606
- from,
607
- to,
608
- editor
609
- ]);
622
+ }, [to, editor]);
623
+ const staged = pending !== null;
624
+ useEffect(() => {
625
+ if (!staged) return;
626
+ const view = editor.view;
627
+ const position = getPendingReplacement(view.state)?.to;
628
+ if (position == null) return;
629
+ const clearanceBottom = window.innerHeight - PREVIEW_CLEARANCE;
630
+ let coords = view.coordsAtPos(position);
631
+ if (coords.top >= 0 && coords.bottom <= clearanceBottom) return;
632
+ const { node } = view.domAtPos(position);
633
+ const element = node instanceof Element ? node : node.parentElement;
634
+ if (!element) return;
635
+ element.scrollIntoView({ block: "center" });
636
+ coords = view.coordsAtPos(position);
637
+ const overflow = coords.bottom - clearanceBottom;
638
+ const nudge = Math.min(overflow, Math.max(0, coords.top - SCROLL_TOP_MARGIN));
639
+ if (nudge > 0) {
640
+ const scroller = closestScrollable(element);
641
+ if (scroller) scroller.scrollTop += nudge;
642
+ }
643
+ }, [staged, editor]);
610
644
  if (!pending) return null;
611
645
  const discard = () => {
612
646
  editor.commands.discardPendingReplacement();
@@ -656,7 +690,7 @@ function PendingReplacementPreview({ actions, onResolve }) {
656
690
  "data-testid": "pending-replacement-accept",
657
691
  disabled: !pending.text.trim(),
658
692
  onClick: accept,
659
- children: "Accept"
693
+ children: pending.mode === "replace" ? "Replace selection" : "Insert below"
660
694
  })
661
695
  ]
662
696
  })]
@@ -1037,7 +1071,13 @@ var table_handle_module_default = {
1037
1071
  //#region src/components/table-handle.tsx
1038
1072
  function getTableHandleState(editor) {
1039
1073
  const commands = editor.commands;
1074
+ const columnAlign = getTableColumnAlign(editor.state);
1040
1075
  return {
1076
+ columnAlign,
1077
+ setTableColumnAlign: {
1078
+ canExec: commands.setTableColumnAlign.canExec("left"),
1079
+ command: (align) => commands.setTableColumnAlign(columnAlign === align ? null : align)
1080
+ },
1041
1081
  addTableColumnBefore: {
1042
1082
  canExec: commands.addTableColumnBefore.canExec(),
1043
1083
  command: () => commands.addTableColumnBefore()
@@ -1072,6 +1112,21 @@ function getTableHandleState(editor) {
1072
1112
  }
1073
1113
  };
1074
1114
  }
1115
+ const COLUMN_ALIGN_LABELS = {
1116
+ left: "Align Left",
1117
+ center: "Align Center",
1118
+ right: "Align Right"
1119
+ };
1120
+ function ColumnAlignMenuItem({ align, columnAlign, onSelect }) {
1121
+ const active = columnAlign === align;
1122
+ return /* @__PURE__ */ jsxs(MenuItem, {
1123
+ className: table_handle_module_default.MenuItem,
1124
+ "data-testid": `table-align-${align}`,
1125
+ "data-active": active ? "" : void 0,
1126
+ onSelect,
1127
+ children: [/* @__PURE__ */ jsx("span", { children: COLUMN_ALIGN_LABELS[align] }), active && /* @__PURE__ */ jsx(CheckIcon, {})]
1128
+ });
1129
+ }
1075
1130
  function TableHandle() {
1076
1131
  const state = useEditorDerivedValue(getTableHandleState);
1077
1132
  return /* @__PURE__ */ jsxs(TableHandleRoot, { children: [
@@ -1103,6 +1158,23 @@ function TableHandle() {
1103
1158
  onSelect: state.addTableColumnAfter.command,
1104
1159
  children: /* @__PURE__ */ jsx("span", { children: "Insert Right" })
1105
1160
  }),
1161
+ state.setTableColumnAlign.canExec && /* @__PURE__ */ jsxs(Fragment$1, { children: [
1162
+ /* @__PURE__ */ jsx(ColumnAlignMenuItem, {
1163
+ align: "left",
1164
+ columnAlign: state.columnAlign,
1165
+ onSelect: () => state.setTableColumnAlign.command("left")
1166
+ }),
1167
+ /* @__PURE__ */ jsx(ColumnAlignMenuItem, {
1168
+ align: "center",
1169
+ columnAlign: state.columnAlign,
1170
+ onSelect: () => state.setTableColumnAlign.command("center")
1171
+ }),
1172
+ /* @__PURE__ */ jsx(ColumnAlignMenuItem, {
1173
+ align: "right",
1174
+ columnAlign: state.columnAlign,
1175
+ onSelect: () => state.setTableColumnAlign.command("right")
1176
+ })
1177
+ ] }),
1106
1178
  state.deleteCellSelection.canExec && /* @__PURE__ */ jsxs(MenuItem, {
1107
1179
  className: table_handle_module_default.MenuItem,
1108
1180
  "data-testid": "table-clear-column",
@@ -1333,7 +1405,10 @@ function resolveSelection(doc, selection) {
1333
1405
  }
1334
1406
  function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, bulletAfterHeading, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, timeFormat, editorClassName, ref, children }) {
1335
1407
  const [editor] = useState(() => {
1336
- const editor = createEditor({ extension: union(defineEditorExtension({ resolveFileLink }), defineCodeBlockView()) });
1408
+ const editor = createEditor({ extension: union(defineEditorExtension({
1409
+ resolveFileLink,
1410
+ markMode
1411
+ }), defineCodeBlockView()) });
1337
1412
  if (initialMarkdown) editor.setContent(markdownToDoc(initialMarkdown, {
1338
1413
  nodes: editor.nodes,
1339
1414
  frontmatter
package/dist/style.css CHANGED
@@ -346,7 +346,7 @@
346
346
  }
347
347
  .meow_Positioner_DPJ5ia {
348
348
  z-index: 50;
349
- width: min(24rem, 100vw - 1rem);
349
+ width: min(30rem, 100vw - 1rem);
350
350
  display: block;
351
351
  }
352
352
 
@@ -377,6 +377,7 @@
377
377
 
378
378
  .meow_Footer_DPJ5ia {
379
379
  border-top: 1px solid var(--meowdown-border);
380
+ flex-wrap: wrap;
380
381
  align-items: center;
381
382
  gap: .25rem;
382
383
  padding: .375rem;
@@ -389,6 +390,7 @@
389
390
 
390
391
  .meow_Button_DPJ5ia {
391
392
  color: var(--meowdown-text);
393
+ white-space: nowrap;
392
394
  cursor: pointer;
393
395
  border-radius: .5rem;
394
396
  align-items: center;
@@ -403,6 +405,7 @@
403
405
  .meow_AcceptButton_DPJ5ia {
404
406
  color: var(--meowdown-popover-bg);
405
407
  background: var(--meowdown-accent);
408
+ white-space: nowrap;
406
409
  cursor: pointer;
407
410
  border-radius: .5rem;
408
411
  align-items: center;
@@ -737,6 +740,13 @@
737
740
  opacity: .8;
738
741
  font-size: .75rem;
739
742
  }
743
+
744
+ & svg {
745
+ width: 1rem;
746
+ height: 1rem;
747
+ color: var(--meowdown-muted);
748
+ display: block;
749
+ }
740
750
  }
741
751
  .meowdown {
742
752
  flex-direction: column;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.33.1",
4
+ "version": "0.35.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,12 +20,12 @@
20
20
  "dependencies": {
21
21
  "@base-ui/react": "^1.6.0",
22
22
  "@ocavue/utils": "^1.7.0",
23
- "@prosekit/core": "^0.13.0-beta.4",
24
- "@prosekit/pm": "^0.1.19-beta.1",
25
- "@prosekit/react": "^0.8.0-beta.15",
23
+ "@prosekit/core": "^0.13.0-beta.5",
24
+ "@prosekit/pm": "^0.1.19-beta.2",
25
+ "@prosekit/react": "^0.8.0-beta.17",
26
26
  "clsx": "^2.1.1",
27
27
  "lucide-react": "^1.21.0",
28
- "@meowdown/core": "0.33.1"
28
+ "@meowdown/core": "0.35.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": "^19.0.0",