@jvs-milkdown/crepe 1.2.16 → 1.2.17

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/lib/esm/index.js CHANGED
@@ -6,7 +6,7 @@ import { $ctx, $nodeSchema, $view, $remark, $markAttr, $markSchema, $command, $p
6
6
  import { defineComponent, shallowRef, ref, computed, h, createApp, watchEffect, watch, onUnmounted, Fragment, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
7
7
  import { Icon } from '@jvs-milkdown/kit/component';
8
8
  import { blockConfig, block, BlockProvider } from '@jvs-milkdown/kit/plugin/block';
9
- import { commandsCtx, editorViewCtx, editorCtx, EditorStatus, schemaCtx, parserCtx, editorViewOptionsCtx, Editor, rootCtx, defaultValueCtx } from '@jvs-milkdown/kit/core';
9
+ import { commandsCtx, editorViewCtx, schemaCtx, editorCtx, EditorStatus, parserCtx, editorViewOptionsCtx, Editor, rootCtx, defaultValueCtx } from '@jvs-milkdown/kit/core';
10
10
  import { findNodeInSelection, findParent, nodeRule } from '@jvs-milkdown/kit/prose';
11
11
  import { NodeSelection, TextSelection, Plugin, PluginKey, EditorState, AllSelection } from '@jvs-milkdown/kit/prose/state';
12
12
  import { computePosition, flip, shift, offset } from '@floating-ui/dom';
@@ -3071,27 +3071,39 @@ const Menu = defineComponent({
3071
3071
  let activeTextColor = null;
3072
3072
  let activeBgColor = null;
3073
3073
  if (node && node.nodeSize > 2) {
3074
- node.descendants((childNode) => {
3075
- if (childNode.isText) {
3076
- const tcType = textColorSchema.type(ctx);
3077
- const bcType = bgColorSchema.type(ctx);
3078
- if (!activeTextColor) {
3079
- const tcMark = childNode.marks.find((m) => m.type === tcType);
3080
- if (tcMark) activeTextColor = tcMark.attrs.color;
3081
- }
3082
- if (!activeBgColor) {
3083
- const bcMark = childNode.marks.find((m) => m.type === bcType);
3084
- if (bcMark) activeBgColor = bcMark.attrs.color;
3074
+ const schema = ctx.get(schemaCtx);
3075
+ const tcHasMark = schema.marks[textColorSchema.id];
3076
+ const bcHasMark = schema.marks[bgColorSchema.id];
3077
+ if (tcHasMark || bcHasMark) {
3078
+ node.descendants((childNode) => {
3079
+ if (childNode.isText) {
3080
+ if (tcHasMark && !activeTextColor) {
3081
+ const tcType = textColorSchema.type(ctx);
3082
+ const tcMark = childNode.marks.find(
3083
+ (m) => m.type === tcType
3084
+ );
3085
+ if (tcMark) activeTextColor = tcMark.attrs.color;
3086
+ }
3087
+ if (bcHasMark && !activeBgColor) {
3088
+ const bcType = bgColorSchema.type(ctx);
3089
+ const bcMark = childNode.marks.find(
3090
+ (m) => m.type === bcType
3091
+ );
3092
+ if (bcMark) activeBgColor = bcMark.attrs.color;
3093
+ }
3085
3094
  }
3086
- }
3087
- return false;
3088
- });
3095
+ return false;
3096
+ });
3097
+ }
3089
3098
  }
3090
3099
  const setBlockColor = (colorValue, isBg) => {
3091
3100
  if (pos === null || pos === void 0) return;
3092
3101
  const { tr } = view.state;
3093
3102
  const targetNode = tr.doc.nodeAt(pos);
3094
3103
  if (!targetNode) return;
3104
+ const schema = ctx.get(schemaCtx);
3105
+ const hasMark = isBg ? schema.marks[bgColorSchema.id] : schema.marks[textColorSchema.id];
3106
+ if (!hasMark) return;
3095
3107
  const markType = isBg ? bgColorSchema.type(ctx) : textColorSchema.type(ctx);
3096
3108
  const start = pos + 1;
3097
3109
  const end = pos + targetNode.nodeSize - 1;
@@ -3110,10 +3122,17 @@ const Menu = defineComponent({
3110
3122
  const { tr } = view.state;
3111
3123
  const targetNode = tr.doc.nodeAt(pos);
3112
3124
  if (!targetNode) return;
3125
+ const schema = ctx.get(schemaCtx);
3126
+ const tcHasMark = schema.marks[textColorSchema.id];
3127
+ const bcHasMark = schema.marks[bgColorSchema.id];
3113
3128
  const start = pos + 1;
3114
3129
  const end = pos + targetNode.nodeSize - 1;
3115
- tr.removeMark(start, end, textColorSchema.type(ctx));
3116
- tr.removeMark(start, end, bgColorSchema.type(ctx));
3130
+ if (tcHasMark) {
3131
+ tr.removeMark(start, end, textColorSchema.type(ctx));
3132
+ }
3133
+ if (bcHasMark) {
3134
+ tr.removeMark(start, end, bgColorSchema.type(ctx));
3135
+ }
3117
3136
  view.dispatch(tr);
3118
3137
  showColorMenu.value = false;
3119
3138
  hide();
@@ -6144,6 +6163,10 @@ const Toolbar = defineComponent({
6144
6163
  return { textColor: null, bgColor: null };
6145
6164
  const view = ctx.get(editorViewCtx);
6146
6165
  const { state } = view;
6166
+ const schema = ctx.get(schemaCtx);
6167
+ const tcHasMark = schema.marks[textColorSchema.id];
6168
+ const bcHasMark = schema.marks[bgColorSchema.id];
6169
+ if (!tcHasMark || !bcHasMark) return { textColor: null, bgColor: null };
6147
6170
  const tcType = textColorSchema.type(ctx);
6148
6171
  const bcType = bgColorSchema.type(ctx);
6149
6172
  const { $cursor, ranges } = state.selection;
@@ -6188,6 +6211,10 @@ const Toolbar = defineComponent({
6188
6211
  const { state, dispatch } = view;
6189
6212
  const { tr } = state;
6190
6213
  const { from, to, empty } = state.selection;
6214
+ const schema = ctx.get(schemaCtx);
6215
+ const tcHasMark = schema.marks[textColorSchema.id];
6216
+ const bcHasMark = schema.marks[bgColorSchema.id];
6217
+ if (!tcHasMark || !bcHasMark) return;
6191
6218
  const textColorType = textColorSchema.type(ctx);
6192
6219
  const bgColorType = bgColorSchema.type(ctx);
6193
6220
  if (empty) {
@@ -6209,6 +6236,10 @@ const Toolbar = defineComponent({
6209
6236
  return { fontFamily: null, fontSize: null };
6210
6237
  const view = ctx.get(editorViewCtx);
6211
6238
  const { state } = view;
6239
+ const schema = ctx.get(schemaCtx);
6240
+ const ffHasMark = schema.marks[fontFamilySchema.id];
6241
+ const fsHasMark = schema.marks[fontSizeSchema.id];
6242
+ if (!ffHasMark || !fsHasMark) return { fontFamily: null, fontSize: null };
6212
6243
  const ffType = fontFamilySchema.type(ctx);
6213
6244
  const fsType = fontSizeSchema.type(ctx);
6214
6245
  const { $cursor, ranges } = state.selection;
@@ -9184,6 +9215,11 @@ const OutlinePanel = defineComponent({
9184
9215
  const activeId = ref("");
9185
9216
  const collapsedIds = ref(/* @__PURE__ */ new Set());
9186
9217
  let scrollLock = false;
9218
+ const clickedActiveId = ref(null);
9219
+ const clearClickedActive = () => {
9220
+ if (scrollLock) return;
9221
+ clickedActiveId.value = null;
9222
+ };
9187
9223
  const hasChildren = (index) => {
9188
9224
  const current = items.value[index];
9189
9225
  if (!current) return false;
@@ -9238,6 +9274,10 @@ const OutlinePanel = defineComponent({
9238
9274
  if (scrollLock) return;
9239
9275
  const view = props.ctx.get(editorViewCtx);
9240
9276
  if (!view || !view.dom) return;
9277
+ if (clickedActiveId.value) {
9278
+ activeId.value = clickedActiveId.value;
9279
+ return;
9280
+ }
9241
9281
  const headings = Array.from(
9242
9282
  view.dom.querySelectorAll("h1, h2, h3, h4, h5, h6")
9243
9283
  );
@@ -9245,14 +9285,39 @@ const OutlinePanel = defineComponent({
9245
9285
  activeId.value = "";
9246
9286
  return;
9247
9287
  }
9248
- const scrollContainer = view.dom.parentElement;
9249
- if (!scrollContainer) return;
9250
- const toolbar = scrollContainer.querySelector(
9288
+ let scrollContainer = view.dom.parentElement;
9289
+ while (scrollContainer && scrollContainer !== document.body) {
9290
+ const style = window.getComputedStyle(scrollContainer);
9291
+ const overflowY = style.overflowY;
9292
+ if ((overflowY === "auto" || overflowY === "scroll") && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
9293
+ break;
9294
+ }
9295
+ scrollContainer = scrollContainer.parentElement;
9296
+ }
9297
+ const isRootScroll = !scrollContainer || scrollContainer === document.body || scrollContainer === document.documentElement;
9298
+ const actualScrollContainer = isRootScroll ? document.scrollingElement || document.documentElement || document.body : scrollContainer;
9299
+ const rootNode = view.dom.getRootNode();
9300
+ let toolbar = rootNode.querySelector(
9251
9301
  ".milkdown-fixed-toolbar"
9252
9302
  );
9303
+ if (!toolbar) {
9304
+ toolbar = document.querySelector(
9305
+ ".milkdown-fixed-toolbar"
9306
+ );
9307
+ }
9253
9308
  const toolbarHeight = (toolbar == null ? void 0 : toolbar.offsetHeight) || 0;
9254
- const containerRect = scrollContainer.getBoundingClientRect();
9255
- const threshold = containerRect.top + toolbarHeight + 50;
9309
+ const containerRect = actualScrollContainer.getBoundingClientRect();
9310
+ const containerTop = isRootScroll ? 0 : Math.max(0, containerRect.top);
9311
+ let scrollTop = actualScrollContainer.scrollTop;
9312
+ let clientHeight = actualScrollContainer.clientHeight;
9313
+ let scrollHeight = actualScrollContainer.scrollHeight;
9314
+ if (isRootScroll) {
9315
+ scrollTop = window.scrollY || document.documentElement.scrollTop;
9316
+ clientHeight = window.innerHeight || document.documentElement.clientHeight;
9317
+ scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
9318
+ }
9319
+ const isAtBottom = scrollTop + clientHeight >= scrollHeight - 15;
9320
+ const threshold = isAtBottom ? containerTop + clientHeight : containerTop + toolbarHeight + 50;
9256
9321
  let active = headings[0];
9257
9322
  for (const h2 of headings) {
9258
9323
  const rect = h2.getBoundingClientRect();
@@ -9262,43 +9327,120 @@ const OutlinePanel = defineComponent({
9262
9327
  break;
9263
9328
  }
9264
9329
  }
9265
- activeId.value = (active == null ? void 0 : active.id) || "";
9330
+ const activeItem = items.value.find((item) => {
9331
+ try {
9332
+ const activePos = view.posAtDOM(active, 0);
9333
+ const isPosMatch = item.pos === activePos - 1;
9334
+ let isDepthMatch = false;
9335
+ const $pos = view.state.doc.resolve(activePos);
9336
+ for (let d = $pos.depth; d >= 0; d--) {
9337
+ if ($pos.node(d).type.name === "heading") {
9338
+ if (item.pos === $pos.before(d)) {
9339
+ isDepthMatch = true;
9340
+ break;
9341
+ }
9342
+ }
9343
+ }
9344
+ if (isPosMatch || isDepthMatch) {
9345
+ return true;
9346
+ }
9347
+ } catch (e) {
9348
+ }
9349
+ const dom = view.nodeDOM(item.pos);
9350
+ const domMatch = dom === active || dom instanceof HTMLElement && active && dom.contains(active);
9351
+ if (domMatch) {
9352
+ return true;
9353
+ }
9354
+ return false;
9355
+ });
9356
+ const newActiveId = activeItem ? activeItem.id : (active == null ? void 0 : active.id) || "";
9357
+ activeId.value = newActiveId;
9266
9358
  };
9267
- const scrollToHeading = (id) => {
9359
+ const scrollToHeading = (item) => {
9268
9360
  const view = props.ctx.get(editorViewCtx);
9269
9361
  if (!view) return;
9270
9362
  try {
9271
- const headingEl = view.dom.querySelector(
9272
- `[id="${id}"]`
9273
- );
9363
+ let headingEl = view.nodeDOM(item.pos);
9364
+ if (!headingEl) {
9365
+ try {
9366
+ const domAt = view.domAtPos(item.pos);
9367
+ let node = domAt.node;
9368
+ if (node.nodeType === Node.TEXT_NODE) {
9369
+ node = node.parentElement;
9370
+ }
9371
+ if (node instanceof HTMLElement) {
9372
+ headingEl = node.closest(
9373
+ "h1, h2, h3, h4, h5, h6"
9374
+ );
9375
+ if (!headingEl && domAt.offset < node.childNodes.length) {
9376
+ const child = node.childNodes[domAt.offset];
9377
+ if (child instanceof HTMLElement) {
9378
+ headingEl = child.closest("h1, h2, h3, h4, h5, h6") || child.querySelector(
9379
+ "h1, h2, h3, h4, h5, h6"
9380
+ );
9381
+ }
9382
+ }
9383
+ }
9384
+ } catch (e) {
9385
+ }
9386
+ }
9387
+ if (!headingEl) {
9388
+ headingEl = view.dom.querySelector(
9389
+ `[id="${item.id}"]`
9390
+ );
9391
+ }
9274
9392
  if (!headingEl) return;
9275
- const scrollContainer = view.dom.parentElement;
9276
- if (!scrollContainer) return;
9393
+ let scrollContainer = view.dom.parentElement;
9394
+ while (scrollContainer && scrollContainer !== document.body) {
9395
+ const style = window.getComputedStyle(scrollContainer);
9396
+ const overflowY = style.overflowY;
9397
+ if ((overflowY === "auto" || overflowY === "scroll") && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
9398
+ break;
9399
+ }
9400
+ scrollContainer = scrollContainer.parentElement;
9401
+ }
9402
+ const isRootScroll = !scrollContainer || scrollContainer === document.body || scrollContainer === document.documentElement;
9403
+ const actualScrollContainer = isRootScroll ? document.scrollingElement || document.documentElement || document.body : scrollContainer;
9277
9404
  scrollLock = true;
9278
- const toolbar = scrollContainer.querySelector(
9405
+ const rootNode = view.dom.getRootNode();
9406
+ let toolbar = rootNode.querySelector(
9279
9407
  ".milkdown-fixed-toolbar"
9280
9408
  );
9409
+ if (!toolbar) {
9410
+ toolbar = document.querySelector(
9411
+ ".milkdown-fixed-toolbar"
9412
+ );
9413
+ }
9281
9414
  const toolbarHeight = (toolbar == null ? void 0 : toolbar.offsetHeight) || 0;
9282
- const containerRect = scrollContainer.getBoundingClientRect();
9415
+ const containerRectTop = isRootScroll ? 0 : actualScrollContainer.getBoundingClientRect().top;
9283
9416
  const headingRect = headingEl.getBoundingClientRect();
9284
- scrollContainer.scrollTo({
9285
- top: scrollContainer.scrollTop + headingRect.top - containerRect.top - toolbarHeight - 20,
9286
- behavior: "smooth"
9287
- });
9417
+ const targetTop = actualScrollContainer.scrollTop + headingRect.top - containerRectTop - toolbarHeight - 20;
9418
+ const scrollTarget = isRootScroll ? window : actualScrollContainer;
9288
9419
  let timer;
9289
9420
  const onScrollEnd = () => {
9290
9421
  clearTimeout(timer);
9291
9422
  timer = setTimeout(() => {
9292
9423
  scrollLock = false;
9293
- scrollContainer.removeEventListener("scroll", onScrollEnd);
9424
+ scrollTarget.removeEventListener("scroll", onScrollEnd);
9294
9425
  checkActive();
9295
9426
  }, 150);
9296
9427
  };
9297
- scrollContainer.addEventListener("scroll", onScrollEnd);
9428
+ scrollTarget.addEventListener("scroll", onScrollEnd);
9298
9429
  setTimeout(() => {
9299
9430
  scrollLock = false;
9300
- scrollContainer.removeEventListener("scroll", onScrollEnd);
9431
+ scrollTarget.removeEventListener("scroll", onScrollEnd);
9301
9432
  }, 2e3);
9433
+ if (isRootScroll) {
9434
+ window.scrollTo({
9435
+ top: targetTop,
9436
+ behavior: "smooth"
9437
+ });
9438
+ } else {
9439
+ actualScrollContainer.scrollTo({
9440
+ top: targetTop,
9441
+ behavior: "smooth"
9442
+ });
9443
+ }
9302
9444
  } catch (e) {
9303
9445
  scrollLock = false;
9304
9446
  updateOutline();
@@ -9309,6 +9451,27 @@ const OutlinePanel = defineComponent({
9309
9451
  onMounted(() => {
9310
9452
  updateOutline();
9311
9453
  checkActive();
9454
+ window.addEventListener("wheel", clearClickedActive, { passive: true });
9455
+ window.addEventListener("touchmove", clearClickedActive, {
9456
+ passive: true
9457
+ });
9458
+ window.addEventListener("pointerdown", clearClickedActive, {
9459
+ passive: true
9460
+ });
9461
+ const keyHandler = (e) => {
9462
+ if ([
9463
+ "ArrowUp",
9464
+ "ArrowDown",
9465
+ "PageUp",
9466
+ "PageDown",
9467
+ "Home",
9468
+ "End"
9469
+ ].includes(e.key)) {
9470
+ clearClickedActive();
9471
+ }
9472
+ };
9473
+ window.addEventListener("keydown", keyHandler, { passive: true });
9474
+ onMounted._keyHandler = keyHandler;
9312
9475
  interval = setInterval(() => {
9313
9476
  if (viewState.value.outlineVisible) {
9314
9477
  updateOutline();
@@ -9325,6 +9488,13 @@ const OutlinePanel = defineComponent({
9325
9488
  if (interval) clearInterval(interval);
9326
9489
  if (pollInterval) clearInterval(pollInterval);
9327
9490
  window.removeEventListener("scroll", checkActive, true);
9491
+ window.removeEventListener("wheel", clearClickedActive);
9492
+ window.removeEventListener("touchmove", clearClickedActive);
9493
+ window.removeEventListener("pointerdown", clearClickedActive);
9494
+ const keyHandler = onMounted._keyHandler;
9495
+ if (keyHandler) {
9496
+ window.removeEventListener("keydown", keyHandler);
9497
+ }
9328
9498
  });
9329
9499
  let startX = 0;
9330
9500
  let startWidth = 0;
@@ -9452,7 +9622,8 @@ const OutlinePanel = defineComponent({
9452
9622
  key: item.id,
9453
9623
  onClick: () => {
9454
9624
  activeId.value = item.id;
9455
- scrollToHeading(item.id);
9625
+ clickedActiveId.value = item.id;
9626
+ scrollToHeading(item);
9456
9627
  },
9457
9628
  style: {
9458
9629
  display: "flex",
@@ -9509,7 +9680,7 @@ const OutlinePanel = defineComponent({
9509
9680
  e.currentTarget.style.backgroundColor = "transparent";
9510
9681
  }
9511
9682
  },
9512
- "\uFFFD?",
9683
+ "\u25BC",
9513
9684
  " "
9514
9685
  ),
9515
9686
  /* @__PURE__ */ h(