@jvs-milkdown/crepe 1.2.25 → 1.2.26

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
@@ -7,7 +7,7 @@ import { $ctx, $nodeSchema, $view, $remark, $markAttr, $markSchema, $command, $p
7
7
  import { defineComponent, shallowRef, ref, computed, h, createApp, watchEffect, watch, onUnmounted, Fragment, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
8
8
  import { Icon } from '@jvs-milkdown/kit/component';
9
9
  import { blockConfig, block, BlockProvider } from '@jvs-milkdown/kit/plugin/block';
10
- import { commandsCtx, editorViewCtx, schemaCtx, editorCtx, EditorStatus, parserCtx, editorViewOptionsCtx, Editor, rootCtx, defaultValueCtx } from '@jvs-milkdown/kit/core';
10
+ import { commandsCtx, editorViewCtx, schemaCtx, editorCtx, EditorStatus, rootCtx, parserCtx, editorViewOptionsCtx, Editor, defaultValueCtx } from '@jvs-milkdown/kit/core';
11
11
  import { findNodeInSelection, findParent, nodeRule } from '@jvs-milkdown/kit/prose';
12
12
  import { NodeSelection, TextSelection, Plugin, PluginKey, EditorState, AllSelection } from '@jvs-milkdown/kit/prose/state';
13
13
  import { computePosition, flip, shift, offset } from '@floating-ui/dom';
@@ -9096,7 +9096,17 @@ function buildDefaultFixedToolbar(builder, _config, ctx) {
9096
9096
  active: () => false,
9097
9097
  onRun: (ctx2) => {
9098
9098
  const markdown = getMarkdown()(ctx2);
9099
- if (_config == null ? void 0 : _config.onExport) {
9099
+ const view = ctx2.get(editorViewCtx);
9100
+ const root = ctx2.get(rootCtx);
9101
+ const rootNode = view.dom.getRootNode();
9102
+ if ((_config == null ? void 0 : _config.exportItems) && _config.exportItems.length > 0) {
9103
+ const exportItems = _config.exportItems;
9104
+ if (exportItems.length === 1) {
9105
+ handleDirectExport(exportItems[0], markdown, view, root);
9106
+ } else {
9107
+ showDownloadPopover(rootNode, root, view, markdown, exportItems);
9108
+ }
9109
+ } else if (_config == null ? void 0 : _config.onExport) {
9100
9110
  _config.onExport(markdown, ctx2);
9101
9111
  } else {
9102
9112
  const blob = new Blob([markdown], {
@@ -9118,7 +9128,17 @@ function buildDefaultFixedToolbar(builder, _config, ctx) {
9118
9128
  icon: importIcon,
9119
9129
  active: () => false,
9120
9130
  onRun: (ctx2) => {
9121
- if (_config == null ? void 0 : _config.onImport) {
9131
+ const view = ctx2.get(editorViewCtx);
9132
+ const root = ctx2.get(rootCtx);
9133
+ const rootNode = view.dom.getRootNode();
9134
+ if ((_config == null ? void 0 : _config.importItems) && _config.importItems.length > 0) {
9135
+ const importItems = _config.importItems;
9136
+ if (importItems.length === 1) {
9137
+ handleDirectImport(importItems[0], root, ctx2);
9138
+ } else {
9139
+ showImportPopover(rootNode, root, ctx2, importItems);
9140
+ }
9141
+ } else if (_config == null ? void 0 : _config.onImport) {
9122
9142
  _config.onImport((markdown) => replaceAll(markdown)(ctx2), ctx2);
9123
9143
  } else {
9124
9144
  const input = document.createElement("input");
@@ -9149,6 +9169,388 @@ function buildDefaultFixedToolbar(builder, _config, ctx) {
9149
9169
  });
9150
9170
  return builder.build();
9151
9171
  }
9172
+ const activeDownloadPopovers = /* @__PURE__ */ new WeakMap();
9173
+ const activeImportPopovers = /* @__PURE__ */ new WeakMap();
9174
+ const ensureStyles = (rootNode) => {
9175
+ const target = rootNode instanceof ShadowRoot ? rootNode : document.head;
9176
+ if (target.querySelector("#download-popover-styles")) return;
9177
+ const styleEl = document.createElement("style");
9178
+ styleEl.id = "download-popover-styles";
9179
+ styleEl.textContent = `
9180
+ .download-popover, .import-popover {
9181
+ position: fixed;
9182
+ z-index: 10000;
9183
+ width: 140px;
9184
+ padding: 6px 0;
9185
+ display: flex;
9186
+ flex-direction: column;
9187
+ background-color: var(--crepe-color-surface, #ffffff);
9188
+ border: 1px solid var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%));
9189
+ border-radius: 8px;
9190
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
9191
+ opacity: 0;
9192
+ transform: translateY(-8px);
9193
+ transition: opacity 0.15s ease, transform 0.15s ease;
9194
+ pointer-events: none;
9195
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
9196
+ }
9197
+ .download-popover.show, .import-popover.show {
9198
+ opacity: 1;
9199
+ transform: translateY(0);
9200
+ pointer-events: auto;
9201
+ }
9202
+ .download-popover-item, .import-popover-item {
9203
+ cursor: pointer;
9204
+ padding: 8px 14px;
9205
+ display: flex;
9206
+ align-items: center;
9207
+ gap: 8px;
9208
+ font-size: 13px;
9209
+ font-weight: 500;
9210
+ color: var(--crepe-color-primary, #363B4C);
9211
+ transition: background-color 0.2s;
9212
+ user-select: none;
9213
+ }
9214
+ .download-popover-item:hover, .import-popover-item:hover {
9215
+ background-color: var(--crepe-color-hover, #f5f5f5);
9216
+ }
9217
+ .download-popover-item-icon, .import-popover-item-icon {
9218
+ display: flex;
9219
+ align-items: center;
9220
+ justify-content: center;
9221
+ width: 14px;
9222
+ height: 14px;
9223
+ color: var(--crepe-color-primary, #363B4C);
9224
+ opacity: 0.8;
9225
+ }
9226
+ `;
9227
+ target.appendChild(styleEl);
9228
+ };
9229
+ function handleDirectExport(type, markdown, view, root) {
9230
+ if (type === "markdown") {
9231
+ const blob = new Blob([markdown], { type: "text/markdown;charset=utf-8;" });
9232
+ const url = URL.createObjectURL(blob);
9233
+ const link = document.createElement("a");
9234
+ link.href = url;
9235
+ link.download = "document.md";
9236
+ link.click();
9237
+ URL.revokeObjectURL(url);
9238
+ }
9239
+ const event = new CustomEvent("download-click", {
9240
+ detail: {
9241
+ type,
9242
+ markdown,
9243
+ html: view.dom.innerHTML || ""
9244
+ },
9245
+ bubbles: true,
9246
+ composed: true
9247
+ });
9248
+ root.dispatchEvent(event);
9249
+ }
9250
+ function handleDirectImport(type, root, ctx) {
9251
+ if (type === "markdown") {
9252
+ const input = document.createElement("input");
9253
+ input.type = "file";
9254
+ input.accept = ".md";
9255
+ input.onchange = (evt) => {
9256
+ var _a;
9257
+ const file = (_a = evt.target.files) == null ? void 0 : _a[0];
9258
+ if (!file) return;
9259
+ file.text().then((text) => {
9260
+ replaceAll(text)(ctx);
9261
+ const event = new CustomEvent("import-click", {
9262
+ detail: { type: "markdown", file },
9263
+ bubbles: true,
9264
+ composed: true
9265
+ });
9266
+ root.dispatchEvent(event);
9267
+ }).catch((err) => {
9268
+ console.error("Failed to read file:", err);
9269
+ });
9270
+ };
9271
+ input.click();
9272
+ } else {
9273
+ const event = new CustomEvent("import-click", {
9274
+ detail: { type },
9275
+ bubbles: true,
9276
+ composed: true
9277
+ });
9278
+ root.dispatchEvent(event);
9279
+ }
9280
+ }
9281
+ function showDownloadPopover(rootNode, root, view, markdown, exportItems) {
9282
+ var _a, _b, _c;
9283
+ const button = root.querySelector('button[data-key="export"]');
9284
+ if (!button) return;
9285
+ const existing = activeDownloadPopovers.get(button);
9286
+ if (existing) {
9287
+ existing.closePopover();
9288
+ return;
9289
+ }
9290
+ ensureStyles(rootNode);
9291
+ const container = rootNode instanceof ShadowRoot ? rootNode : document.body;
9292
+ const popover = document.createElement("div");
9293
+ popover.className = "download-popover";
9294
+ let popoverHtml = "";
9295
+ if (exportItems.includes("markdown")) {
9296
+ popoverHtml += `
9297
+ <div class="download-popover-item" id="download-md-btn">
9298
+ <span class="download-popover-item-icon">
9299
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg>
9300
+ </span>
9301
+ <span>\u4E0B\u8F7D MD</span>
9302
+ </div>
9303
+ `;
9304
+ }
9305
+ if (exportItems.includes("word")) {
9306
+ popoverHtml += `
9307
+ <div class="download-popover-item" id="download-word-btn">
9308
+ <span class="download-popover-item-icon">
9309
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
9310
+ </span>
9311
+ <span>\u4E0B\u8F7D WORD</span>
9312
+ </div>
9313
+ `;
9314
+ }
9315
+ if (exportItems.includes("pdf")) {
9316
+ popoverHtml += `
9317
+ <div class="download-popover-item" id="download-pdf-btn">
9318
+ <span class="download-popover-item-icon">
9319
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>
9320
+ </span>
9321
+ <span>\u4E0B\u8F7D PDF</span>
9322
+ </div>
9323
+ `;
9324
+ }
9325
+ popover.innerHTML = popoverHtml;
9326
+ container.appendChild(popover);
9327
+ activeDownloadPopovers.set(button, popover);
9328
+ const updatePosition = () => {
9329
+ const rect = button.getBoundingClientRect();
9330
+ popover.style.top = `${rect.bottom + 4}px`;
9331
+ popover.style.left = `${rect.left + (rect.width - 140) / 2}px`;
9332
+ };
9333
+ updatePosition();
9334
+ requestAnimationFrame(() => {
9335
+ popover.classList.add("show");
9336
+ });
9337
+ const closePopover = () => {
9338
+ popover.classList.remove("show");
9339
+ activeDownloadPopovers.delete(button);
9340
+ container.removeEventListener("pointerdown", handleContainerClick, true);
9341
+ document.removeEventListener("pointerdown", handleOuterClick, true);
9342
+ window.removeEventListener("resize", updatePosition);
9343
+ popover.addEventListener(
9344
+ "transitionend",
9345
+ () => {
9346
+ popover.remove();
9347
+ },
9348
+ { once: true }
9349
+ );
9350
+ };
9351
+ popover.closePopover = closePopover;
9352
+ const handleContainerClick = (e) => {
9353
+ const target = e.target;
9354
+ if (!popover.contains(target) && target !== button && !button.contains(target)) {
9355
+ closePopover();
9356
+ }
9357
+ };
9358
+ const handleOuterClick = (e) => {
9359
+ const target = e.target;
9360
+ if (target !== root && !root.contains(target)) {
9361
+ closePopover();
9362
+ }
9363
+ };
9364
+ popover.addEventListener("pointerdown", (e) => {
9365
+ e.stopPropagation();
9366
+ });
9367
+ container.addEventListener("pointerdown", handleContainerClick, true);
9368
+ document.addEventListener("pointerdown", handleOuterClick, true);
9369
+ window.addEventListener("resize", updatePosition);
9370
+ (_a = popover.querySelector("#download-md-btn")) == null ? void 0 : _a.addEventListener("click", (e) => {
9371
+ e.stopPropagation();
9372
+ const blob = new Blob([markdown], { type: "text/markdown;charset=utf-8;" });
9373
+ const url = URL.createObjectURL(blob);
9374
+ const link = document.createElement("a");
9375
+ link.href = url;
9376
+ link.download = "document.md";
9377
+ link.click();
9378
+ URL.revokeObjectURL(url);
9379
+ closePopover();
9380
+ const event = new CustomEvent("download-click", {
9381
+ detail: {
9382
+ type: "markdown",
9383
+ markdown,
9384
+ html: view.dom.innerHTML || ""
9385
+ },
9386
+ bubbles: true,
9387
+ composed: true
9388
+ });
9389
+ root.dispatchEvent(event);
9390
+ });
9391
+ (_b = popover.querySelector("#download-word-btn")) == null ? void 0 : _b.addEventListener("click", (e) => {
9392
+ e.stopPropagation();
9393
+ closePopover();
9394
+ const event = new CustomEvent("download-click", {
9395
+ detail: {
9396
+ type: "word",
9397
+ markdown,
9398
+ html: view.dom.innerHTML || ""
9399
+ },
9400
+ bubbles: true,
9401
+ composed: true
9402
+ });
9403
+ root.dispatchEvent(event);
9404
+ });
9405
+ (_c = popover.querySelector("#download-pdf-btn")) == null ? void 0 : _c.addEventListener("click", (e) => {
9406
+ e.stopPropagation();
9407
+ closePopover();
9408
+ const event = new CustomEvent("download-click", {
9409
+ detail: {
9410
+ type: "pdf",
9411
+ markdown,
9412
+ html: view.dom.innerHTML || ""
9413
+ },
9414
+ bubbles: true,
9415
+ composed: true
9416
+ });
9417
+ root.dispatchEvent(event);
9418
+ });
9419
+ }
9420
+ function showImportPopover(rootNode, root, ctx, importItems) {
9421
+ var _a, _b, _c;
9422
+ const button = root.querySelector('button[data-key="import"]');
9423
+ if (!button) return;
9424
+ const existing = activeImportPopovers.get(button);
9425
+ if (existing) {
9426
+ existing.closePopover();
9427
+ return;
9428
+ }
9429
+ ensureStyles(rootNode);
9430
+ const container = rootNode instanceof ShadowRoot ? rootNode : document.body;
9431
+ const popover = document.createElement("div");
9432
+ popover.className = "import-popover";
9433
+ let popoverHtml = "";
9434
+ if (importItems.includes("markdown")) {
9435
+ popoverHtml += `
9436
+ <div class="import-popover-item" id="import-md-btn">
9437
+ <span class="import-popover-item-icon">
9438
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg>
9439
+ </span>
9440
+ <span>\u5BFC\u5165 MD</span>
9441
+ </div>
9442
+ `;
9443
+ }
9444
+ if (importItems.includes("word")) {
9445
+ popoverHtml += `
9446
+ <div class="import-popover-item" id="import-word-btn">
9447
+ <span class="import-popover-item-icon">
9448
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
9449
+ </span>
9450
+ <span>\u5BFC\u5165 WORD</span>
9451
+ </div>
9452
+ `;
9453
+ }
9454
+ if (importItems.includes("pdf")) {
9455
+ popoverHtml += `
9456
+ <div class="import-popover-item" id="import-pdf-btn">
9457
+ <span class="import-popover-item-icon">
9458
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>
9459
+ </span>
9460
+ <span>\u5BFC\u5165 PDF</span>
9461
+ </div>
9462
+ `;
9463
+ }
9464
+ popover.innerHTML = popoverHtml;
9465
+ container.appendChild(popover);
9466
+ activeImportPopovers.set(button, popover);
9467
+ const updatePosition = () => {
9468
+ const rect = button.getBoundingClientRect();
9469
+ popover.style.top = `${rect.bottom + 4}px`;
9470
+ popover.style.left = `${rect.left + (rect.width - 140) / 2}px`;
9471
+ };
9472
+ updatePosition();
9473
+ requestAnimationFrame(() => {
9474
+ popover.classList.add("show");
9475
+ });
9476
+ const closePopover = () => {
9477
+ popover.classList.remove("show");
9478
+ activeImportPopovers.delete(button);
9479
+ container.removeEventListener("pointerdown", handleContainerClick, true);
9480
+ document.removeEventListener("pointerdown", handleOuterClick, true);
9481
+ window.removeEventListener("resize", updatePosition);
9482
+ popover.addEventListener(
9483
+ "transitionend",
9484
+ () => {
9485
+ popover.remove();
9486
+ },
9487
+ { once: true }
9488
+ );
9489
+ };
9490
+ popover.closePopover = closePopover;
9491
+ const handleContainerClick = (e) => {
9492
+ const target = e.target;
9493
+ if (!popover.contains(target) && target !== button && !button.contains(target)) {
9494
+ closePopover();
9495
+ }
9496
+ };
9497
+ const handleOuterClick = (e) => {
9498
+ const target = e.target;
9499
+ if (target !== root && !root.contains(target)) {
9500
+ closePopover();
9501
+ }
9502
+ };
9503
+ popover.addEventListener("pointerdown", (e) => {
9504
+ e.stopPropagation();
9505
+ });
9506
+ container.addEventListener("pointerdown", handleContainerClick, true);
9507
+ document.addEventListener("pointerdown", handleOuterClick, true);
9508
+ window.addEventListener("resize", updatePosition);
9509
+ (_a = popover.querySelector("#import-md-btn")) == null ? void 0 : _a.addEventListener("click", (e) => {
9510
+ e.stopPropagation();
9511
+ closePopover();
9512
+ const input = document.createElement("input");
9513
+ input.type = "file";
9514
+ input.accept = ".md";
9515
+ input.onchange = (evt) => {
9516
+ var _a2;
9517
+ const file = (_a2 = evt.target.files) == null ? void 0 : _a2[0];
9518
+ if (!file) return;
9519
+ file.text().then((text) => {
9520
+ replaceAll(text)(ctx);
9521
+ const event = new CustomEvent("import-click", {
9522
+ detail: { type: "markdown", file },
9523
+ bubbles: true,
9524
+ composed: true
9525
+ });
9526
+ root.dispatchEvent(event);
9527
+ }).catch((err) => {
9528
+ console.error("Failed to read file:", err);
9529
+ });
9530
+ };
9531
+ input.click();
9532
+ });
9533
+ (_b = popover.querySelector("#import-word-btn")) == null ? void 0 : _b.addEventListener("click", (e) => {
9534
+ e.stopPropagation();
9535
+ closePopover();
9536
+ const event = new CustomEvent("import-click", {
9537
+ detail: { type: "word" },
9538
+ bubbles: true,
9539
+ composed: true
9540
+ });
9541
+ root.dispatchEvent(event);
9542
+ });
9543
+ (_c = popover.querySelector("#import-pdf-btn")) == null ? void 0 : _c.addEventListener("click", (e) => {
9544
+ e.stopPropagation();
9545
+ closePopover();
9546
+ const event = new CustomEvent("import-click", {
9547
+ detail: { type: "pdf" },
9548
+ bubbles: true,
9549
+ composed: true
9550
+ });
9551
+ root.dispatchEvent(event);
9552
+ });
9553
+ }
9152
9554
 
9153
9555
  keepAlive(h);
9154
9556
  const DocumentHeader = defineComponent({