@select-org/select-post-builder 1.1.0 → 1.1.1

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.
@@ -21042,6 +21042,46 @@ const EditMediaToolbar = ({
21042
21042
  }
21043
21043
  )
21044
21044
  ] }) : null;
21045
+ var NAME = "AspectRatio";
21046
+ var AspectRatio$1 = React__namespace.forwardRef(
21047
+ (props, forwardedRef) => {
21048
+ const { ratio = 1 / 1, style, ...aspectRatioProps } = props;
21049
+ return /* @__PURE__ */ jsxRuntime.jsx(
21050
+ "div",
21051
+ {
21052
+ style: {
21053
+ // ensures inner element is contained
21054
+ position: "relative",
21055
+ // ensures padding bottom trick maths works
21056
+ width: "100%",
21057
+ paddingBottom: `${100 / ratio}%`
21058
+ },
21059
+ "data-radix-aspect-ratio-wrapper": "",
21060
+ children: /* @__PURE__ */ jsxRuntime.jsx(
21061
+ Primitive.div,
21062
+ {
21063
+ ...aspectRatioProps,
21064
+ ref: forwardedRef,
21065
+ style: {
21066
+ ...style,
21067
+ // ensures children expand in ratio
21068
+ position: "absolute",
21069
+ top: 0,
21070
+ right: 0,
21071
+ bottom: 0,
21072
+ left: 0
21073
+ }
21074
+ }
21075
+ )
21076
+ }
21077
+ );
21078
+ }
21079
+ );
21080
+ AspectRatio$1.displayName = NAME;
21081
+ var Root$1 = AspectRatio$1;
21082
+ function AspectRatio({ ...props }) {
21083
+ return /* @__PURE__ */ jsxRuntime.jsx(Root$1, { "data-slot": "aspect-ratio", ...props });
21084
+ }
21045
21085
  var PROGRESS_NAME = "Progress";
21046
21086
  var DEFAULT_MAX = 100;
21047
21087
  var [createProgressContext] = createContextScope(PROGRESS_NAME);
@@ -21126,11 +21166,11 @@ function getInvalidValueError(propValue, componentName) {
21126
21166
 
21127
21167
  Defaulting to \`null\`.`;
21128
21168
  }
21129
- var Root$1 = Progress$1;
21169
+ var Root = Progress$1;
21130
21170
  var Indicator = ProgressIndicator;
21131
21171
  function Progress({ className, value, ...props }) {
21132
21172
  return /* @__PURE__ */ jsxRuntime.jsx(
21133
- Root$1,
21173
+ Root,
21134
21174
  {
21135
21175
  "data-slot": "progress",
21136
21176
  className: cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className),
@@ -21146,1709 +21186,1661 @@ function Progress({ className, value, ...props }) {
21146
21186
  }
21147
21187
  );
21148
21188
  }
21149
- const lower16 = 65535;
21150
- const factor16 = Math.pow(2, 16);
21151
- function makeRecover(index2, offset2) {
21152
- return index2 + offset2 * factor16;
21153
- }
21154
- function recoverIndex(value) {
21155
- return value & lower16;
21156
- }
21157
- function recoverOffset(value) {
21158
- return (value - (value & lower16)) / factor16;
21159
- }
21160
- const DEL_BEFORE = 1, DEL_AFTER = 2, DEL_ACROSS = 4, DEL_SIDE = 8;
21161
- class MapResult {
21162
- /**
21163
- @internal
21164
- */
21165
- constructor(pos, delInfo, recover) {
21166
- this.pos = pos;
21167
- this.delInfo = delInfo;
21168
- this.recover = recover;
21169
- }
21170
- /**
21171
- Tells you whether the position was deleted, that is, whether the
21172
- step removed the token on the side queried (via the `assoc`)
21173
- argument from the document.
21174
- */
21175
- get deleted() {
21176
- return (this.delInfo & DEL_SIDE) > 0;
21177
- }
21178
- /**
21179
- Tells you whether the token before the mapped position was deleted.
21180
- */
21181
- get deletedBefore() {
21182
- return (this.delInfo & (DEL_BEFORE | DEL_ACROSS)) > 0;
21183
- }
21184
- /**
21185
- True when the token after the mapped position was deleted.
21186
- */
21187
- get deletedAfter() {
21188
- return (this.delInfo & (DEL_AFTER | DEL_ACROSS)) > 0;
21189
- }
21190
- /**
21191
- Tells whether any of the steps mapped through deletes across the
21192
- position (including both the token before and after the
21193
- position).
21194
- */
21195
- get deletedAcross() {
21196
- return (this.delInfo & DEL_ACROSS) > 0;
21197
- }
21198
- }
21199
- class StepMap {
21200
- /**
21201
- Create a position map. The modifications to the document are
21202
- represented as an array of numbers, in which each group of three
21203
- represents a modified chunk as `[start, oldSize, newSize]`.
21204
- */
21205
- constructor(ranges, inverted = false) {
21206
- this.ranges = ranges;
21207
- this.inverted = inverted;
21208
- if (!ranges.length && StepMap.empty)
21209
- return StepMap.empty;
21210
- }
21211
- /**
21212
- @internal
21213
- */
21214
- recover(value) {
21215
- let diff = 0, index2 = recoverIndex(value);
21216
- if (!this.inverted)
21217
- for (let i = 0; i < index2; i++)
21218
- diff += this.ranges[i * 3 + 2] - this.ranges[i * 3 + 1];
21219
- return this.ranges[index2 * 3] + diff + recoverOffset(value);
21220
- }
21221
- mapResult(pos, assoc = 1) {
21222
- return this._map(pos, assoc, false);
21223
- }
21224
- map(pos, assoc = 1) {
21225
- return this._map(pos, assoc, true);
21226
- }
21227
- /**
21228
- @internal
21229
- */
21230
- _map(pos, assoc, simple) {
21231
- let diff = 0, oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21232
- for (let i = 0; i < this.ranges.length; i += 3) {
21233
- let start2 = this.ranges[i] - (this.inverted ? diff : 0);
21234
- if (start2 > pos)
21235
- break;
21236
- let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex], end2 = start2 + oldSize;
21237
- if (pos <= end2) {
21238
- let side = !oldSize ? assoc : pos == start2 ? -1 : pos == end2 ? 1 : assoc;
21239
- let result = start2 + diff + (side < 0 ? 0 : newSize);
21240
- if (simple)
21241
- return result;
21242
- let recover = pos == (assoc < 0 ? start2 : end2) ? null : makeRecover(i / 3, pos - start2);
21243
- let del = pos == start2 ? DEL_AFTER : pos == end2 ? DEL_BEFORE : DEL_ACROSS;
21244
- if (assoc < 0 ? pos != start2 : pos != end2)
21245
- del |= DEL_SIDE;
21246
- return new MapResult(result, del, recover);
21189
+ const MediaNodeView = ({
21190
+ node,
21191
+ updateAttributes,
21192
+ editor,
21193
+ selected,
21194
+ getPos
21195
+ }) => {
21196
+ const handleDelete = () => {
21197
+ const pos = getPos() ?? NaN;
21198
+ if (!Number.isFinite(pos)) return;
21199
+ editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).focus().run();
21200
+ };
21201
+ const handleAlign = (align) => updateAttributes({ align });
21202
+ if (node.attrs.uploading) {
21203
+ return /* @__PURE__ */ jsxRuntime.jsx(
21204
+ react.NodeViewWrapper,
21205
+ {
21206
+ className: cn(
21207
+ "flex my-4",
21208
+ node.attrs.align === Alignment.CENTER ? "justify-center" : node.attrs.align === Alignment.RIGHT ? "justify-end" : ""
21209
+ ),
21210
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-full", children: [
21211
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative bg-muted rounded-lg overflow-hidden", children: [
21212
+ /* @__PURE__ */ jsxRuntime.jsx(
21213
+ AspectRatio,
21214
+ {
21215
+ ratio: node.attrs.type === MediaNodeTypes.Audio ? 4 / 1 : 16 / 4,
21216
+ className: cn("flex items-center justify-center bg-muted"),
21217
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center space-y-3", children: [
21218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center", children: [
21219
+ node.attrs.type === MediaNodeTypes.Image && /* @__PURE__ */ jsxRuntime.jsx(Image$1, { className: "w-12 h-12 text-muted-foreground/50" }),
21220
+ node.attrs.type === MediaNodeTypes.Video && /* @__PURE__ */ jsxRuntime.jsx(Video, { className: "w-12 h-12 text-muted-foreground/50" }),
21221
+ node.attrs.type === MediaNodeTypes.Audio && /* @__PURE__ */ jsxRuntime.jsx(Music, { className: "w-12 h-12 text-muted-foreground/50" })
21222
+ ] }),
21223
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground font-medium", children: [
21224
+ "Uploading ",
21225
+ node.attrs.type,
21226
+ "..."
21227
+ ] })
21228
+ ] })
21229
+ }
21230
+ ),
21231
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(Progress, { value: node.attrs.progress ?? 0, className: "h-2" }) }),
21232
+ /* @__PURE__ */ jsxRuntime.jsx(
21233
+ "button",
21234
+ {
21235
+ onClick: handleDelete,
21236
+ className: "absolute top-2 right-2 p-2 bg-background/90 hover:bg-background rounded-full shadow-sm transition-colors",
21237
+ title: "Cancel upload",
21238
+ children: /* @__PURE__ */ jsxRuntime.jsx(X, { className: "w-4 h-4" })
21239
+ }
21240
+ )
21241
+ ] }),
21242
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground text-center mt-2", children: [
21243
+ Math.round(node.attrs.progress ?? 0),
21244
+ "%"
21245
+ ] })
21246
+ ] })
21247
21247
  }
21248
- diff += newSize - oldSize;
21249
- }
21250
- return simple ? pos + diff : new MapResult(pos + diff, 0, null);
21251
- }
21252
- /**
21253
- @internal
21254
- */
21255
- touches(pos, recover) {
21256
- let diff = 0, index2 = recoverIndex(recover);
21257
- let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21258
- for (let i = 0; i < this.ranges.length; i += 3) {
21259
- let start2 = this.ranges[i] - (this.inverted ? diff : 0);
21260
- if (start2 > pos)
21261
- break;
21262
- let oldSize = this.ranges[i + oldIndex], end2 = start2 + oldSize;
21263
- if (pos <= end2 && i == index2 * 3)
21264
- return true;
21265
- diff += this.ranges[i + newIndex] - oldSize;
21266
- }
21267
- return false;
21268
- }
21269
- /**
21270
- Calls the given function on each of the changed ranges included in
21271
- this map.
21272
- */
21273
- forEach(f) {
21274
- let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21275
- for (let i = 0, diff = 0; i < this.ranges.length; i += 3) {
21276
- let start2 = this.ranges[i], oldStart = start2 - (this.inverted ? diff : 0), newStart = start2 + (this.inverted ? 0 : diff);
21277
- let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex];
21278
- f(oldStart, oldStart + oldSize, newStart, newStart + newSize);
21279
- diff += newSize - oldSize;
21280
- }
21281
- }
21282
- /**
21283
- Create an inverted version of this map. The result can be used to
21284
- map positions in the post-step document to the pre-step document.
21285
- */
21286
- invert() {
21287
- return new StepMap(this.ranges, !this.inverted);
21288
- }
21289
- /**
21290
- @internal
21291
- */
21292
- toString() {
21293
- return (this.inverted ? "-" : "") + JSON.stringify(this.ranges);
21294
- }
21295
- /**
21296
- Create a map that moves all positions by offset `n` (which may be
21297
- negative). This can be useful when applying steps meant for a
21298
- sub-document to a larger document, or vice-versa.
21299
- */
21300
- static offset(n) {
21301
- return n == 0 ? StepMap.empty : new StepMap(n < 0 ? [0, -n, 0] : [0, 0, n]);
21248
+ );
21302
21249
  }
21303
- }
21304
- StepMap.empty = new StepMap([]);
21305
- const stepsByID = /* @__PURE__ */ Object.create(null);
21306
- class Step {
21307
- /**
21308
- Get the step map that represents the changes made by this step,
21309
- and which can be used to transform between positions in the old
21310
- and the new document.
21311
- */
21312
- getMap() {
21313
- return StepMap.empty;
21314
- }
21315
- /**
21316
- Try to merge this step with another one, to be applied directly
21317
- after it. Returns the merged step when possible, null if the
21318
- steps can't be merged.
21319
- */
21320
- merge(other) {
21321
- return null;
21322
- }
21323
- /**
21324
- Deserialize a step from its JSON representation. Will call
21325
- through to the step class' own implementation of this method.
21326
- */
21327
- static fromJSON(schema, json) {
21328
- if (!json || !json.stepType)
21329
- throw new RangeError("Invalid input for Step.fromJSON");
21330
- let type = stepsByID[json.stepType];
21331
- if (!type)
21332
- throw new RangeError(`No step type ${json.stepType} defined`);
21333
- return type.fromJSON(schema, json);
21334
- }
21335
- /**
21336
- To be able to serialize steps to JSON, each step needs a string
21337
- ID to attach to its JSON representation. Use this method to
21338
- register an ID for your step classes. Try to pick something
21339
- that's unlikely to clash with steps from other modules.
21340
- */
21341
- static jsonID(id, stepClass) {
21342
- if (id in stepsByID)
21343
- throw new RangeError("Duplicate use of step JSON ID " + id);
21344
- stepsByID[id] = stepClass;
21345
- stepClass.prototype.jsonID = id;
21346
- return stepClass;
21347
- }
21348
- }
21349
- class StepResult {
21350
- /**
21351
- @internal
21352
- */
21353
- constructor(doc, failed) {
21354
- this.doc = doc;
21355
- this.failed = failed;
21356
- }
21357
- /**
21358
- Create a successful step result.
21359
- */
21360
- static ok(doc) {
21361
- return new StepResult(doc, null);
21362
- }
21363
- /**
21364
- Create a failed step result.
21365
- */
21366
- static fail(message) {
21367
- return new StepResult(null, message);
21368
- }
21369
- /**
21370
- Call [`Node.replace`](https://prosemirror.net/docs/ref/#model.Node.replace) with the given
21371
- arguments. Create a successful result if it succeeds, and a
21372
- failed one if it throws a `ReplaceError`.
21373
- */
21374
- static fromReplace(doc, from, to, slice) {
21375
- try {
21376
- return StepResult.ok(doc.replace(from, to, slice));
21377
- } catch (e) {
21378
- if (e instanceof ReplaceError)
21379
- return StepResult.fail(e.message);
21380
- throw e;
21250
+ return /* @__PURE__ */ jsxRuntime.jsx(
21251
+ react.NodeViewWrapper,
21252
+ {
21253
+ "data-drag-handle": true,
21254
+ className: cn(
21255
+ "flex",
21256
+ node.attrs.align === Alignment.CENTER ? "justify-center" : node.attrs.align === Alignment.RIGHT ? "justify-end" : ""
21257
+ ),
21258
+ children: node.attrs.type === MediaNodeTypes.Image ? /* @__PURE__ */ jsxRuntime.jsx(
21259
+ ResizableWithToolbarWrapper,
21260
+ {
21261
+ open: selected,
21262
+ mediaType: node.attrs.type,
21263
+ align: node.attrs.align,
21264
+ onResize: (maxWidth) => updateAttributes({ maxWidth }),
21265
+ onAlign: handleAlign,
21266
+ onDelete: handleDelete,
21267
+ children: /* @__PURE__ */ jsxRuntime.jsx(
21268
+ "img",
21269
+ {
21270
+ src: node.attrs.src,
21271
+ alt: node.attrs.caption,
21272
+ className: cn(
21273
+ "object-contain w-full h-auto rounded-lg",
21274
+ selected && "border-2 border-blue-500"
21275
+ )
21276
+ }
21277
+ )
21278
+ }
21279
+ ) : node.attrs.type === MediaNodeTypes.Video ? /* @__PURE__ */ jsxRuntime.jsx(
21280
+ ResizableWithToolbarWrapper,
21281
+ {
21282
+ open: selected,
21283
+ mediaType: node.attrs.type,
21284
+ align: node.attrs.align,
21285
+ onResize: (maxWidth) => updateAttributes({ maxWidth }),
21286
+ onAlign: handleAlign,
21287
+ onDelete: handleDelete,
21288
+ children: /* @__PURE__ */ jsxRuntime.jsx(
21289
+ "video",
21290
+ {
21291
+ src: node.attrs.src,
21292
+ controls: true,
21293
+ className: cn("object-contain w-full h-auto", selected && "border-2 border-blue-500")
21294
+ }
21295
+ )
21296
+ }
21297
+ ) : node.attrs.type === MediaNodeTypes.Audio ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative p-4 bg-accent/20 border border-border/20 w-full", children: [
21298
+ /* @__PURE__ */ jsxRuntime.jsx(
21299
+ EditMediaToolbar,
21300
+ {
21301
+ open: selected,
21302
+ align: node.attrs.align,
21303
+ onAlign: handleAlign,
21304
+ onDelete: handleDelete
21305
+ }
21306
+ ),
21307
+ /* @__PURE__ */ jsxRuntime.jsx(
21308
+ "audio",
21309
+ {
21310
+ src: node.attrs.src,
21311
+ controls: true,
21312
+ className: cn("w-full", selected && "rounded-full border-2 border-blue-500")
21313
+ }
21314
+ )
21315
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-sm text-gray-600", children: "Unsupported media" })
21381
21316
  }
21382
- }
21383
- }
21384
- function mapFragment(fragment, f, parent2) {
21385
- let mapped = [];
21386
- for (let i = 0; i < fragment.childCount; i++) {
21387
- let child = fragment.child(i);
21388
- if (child.content.size)
21389
- child = child.copy(mapFragment(child.content, f, child));
21390
- if (child.isInline)
21391
- child = f(child, parent2, i);
21392
- mapped.push(child);
21393
- }
21394
- return Fragment.fromArray(mapped);
21395
- }
21396
- class AddMarkStep extends Step {
21397
- /**
21398
- Create a mark step.
21399
- */
21400
- constructor(from, to, mark) {
21401
- super();
21402
- this.from = from;
21403
- this.to = to;
21404
- this.mark = mark;
21405
- }
21406
- apply(doc) {
21407
- let oldSlice = doc.slice(this.from, this.to), $from = doc.resolve(this.from);
21408
- let parent2 = $from.node($from.sharedDepth(this.to));
21409
- let slice = new Slice(mapFragment(oldSlice.content, (node, parent3) => {
21410
- if (!node.isAtom || !parent3.type.allowsMarkType(this.mark.type))
21411
- return node;
21412
- return node.mark(this.mark.addToSet(node.marks));
21413
- }, parent2), oldSlice.openStart, oldSlice.openEnd);
21414
- return StepResult.fromReplace(doc, this.from, this.to, slice);
21415
- }
21416
- invert() {
21417
- return new RemoveMarkStep(this.from, this.to, this.mark);
21418
- }
21419
- map(mapping) {
21420
- let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21421
- if (from.deleted && to.deleted || from.pos >= to.pos)
21422
- return null;
21423
- return new AddMarkStep(from.pos, to.pos, this.mark);
21424
- }
21425
- merge(other) {
21426
- if (other instanceof AddMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from)
21427
- return new AddMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
21428
- return null;
21429
- }
21430
- toJSON() {
21431
- return {
21432
- stepType: "addMark",
21433
- mark: this.mark.toJSON(),
21434
- from: this.from,
21435
- to: this.to
21436
- };
21437
- }
21438
- /**
21439
- @internal
21440
- */
21441
- static fromJSON(schema, json) {
21442
- if (typeof json.from != "number" || typeof json.to != "number")
21443
- throw new RangeError("Invalid input for AddMarkStep.fromJSON");
21444
- return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
21445
- }
21446
- }
21447
- Step.jsonID("addMark", AddMarkStep);
21448
- class RemoveMarkStep extends Step {
21449
- /**
21450
- Create a mark-removing step.
21451
- */
21452
- constructor(from, to, mark) {
21453
- super();
21454
- this.from = from;
21455
- this.to = to;
21456
- this.mark = mark;
21457
- }
21458
- apply(doc) {
21459
- let oldSlice = doc.slice(this.from, this.to);
21460
- let slice = new Slice(mapFragment(oldSlice.content, (node) => {
21461
- return node.mark(this.mark.removeFromSet(node.marks));
21462
- }, doc), oldSlice.openStart, oldSlice.openEnd);
21463
- return StepResult.fromReplace(doc, this.from, this.to, slice);
21464
- }
21465
- invert() {
21466
- return new AddMarkStep(this.from, this.to, this.mark);
21467
- }
21468
- map(mapping) {
21469
- let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21470
- if (from.deleted && to.deleted || from.pos >= to.pos)
21471
- return null;
21472
- return new RemoveMarkStep(from.pos, to.pos, this.mark);
21473
- }
21474
- merge(other) {
21475
- if (other instanceof RemoveMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from)
21476
- return new RemoveMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
21477
- return null;
21478
- }
21479
- toJSON() {
21480
- return {
21481
- stepType: "removeMark",
21482
- mark: this.mark.toJSON(),
21483
- from: this.from,
21484
- to: this.to
21485
- };
21486
- }
21487
- /**
21488
- @internal
21489
- */
21490
- static fromJSON(schema, json) {
21491
- if (typeof json.from != "number" || typeof json.to != "number")
21492
- throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");
21493
- return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
21494
- }
21495
- }
21496
- Step.jsonID("removeMark", RemoveMarkStep);
21497
- class AddNodeMarkStep extends Step {
21498
- /**
21499
- Create a node mark step.
21500
- */
21501
- constructor(pos, mark) {
21502
- super();
21503
- this.pos = pos;
21504
- this.mark = mark;
21505
- }
21506
- apply(doc) {
21507
- let node = doc.nodeAt(this.pos);
21508
- if (!node)
21509
- return StepResult.fail("No node at mark step's position");
21510
- let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
21511
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
21512
- }
21513
- invert(doc) {
21514
- let node = doc.nodeAt(this.pos);
21515
- if (node) {
21516
- let newSet = this.mark.addToSet(node.marks);
21517
- if (newSet.length == node.marks.length) {
21518
- for (let i = 0; i < node.marks.length; i++)
21519
- if (!node.marks[i].isInSet(newSet))
21520
- return new AddNodeMarkStep(this.pos, node.marks[i]);
21521
- return new AddNodeMarkStep(this.pos, this.mark);
21317
+ );
21318
+ };
21319
+ const ResizableWithToolbarWrapper = ({
21320
+ open,
21321
+ align,
21322
+ onAlign,
21323
+ onDelete,
21324
+ onResize,
21325
+ mediaType,
21326
+ children
21327
+ }) => {
21328
+ return /* @__PURE__ */ jsxRuntime.jsxs(
21329
+ ResizableWrapper,
21330
+ {
21331
+ onResize,
21332
+ className: cn(mediaType === MediaNodeTypes.Audio && "relative p-4 bg-accent/50"),
21333
+ children: [
21334
+ /* @__PURE__ */ jsxRuntime.jsx(EditMediaToolbar, { open, align, onAlign, onDelete }),
21335
+ children
21336
+ ]
21337
+ }
21338
+ );
21339
+ };
21340
+ const createDeleteByUidCommand = (nodeName) => {
21341
+ return (uid) => ({ state: state2, commands }) => {
21342
+ const { doc } = state2;
21343
+ let targetPos = null;
21344
+ let targetSize = null;
21345
+ doc.descendants((node, pos) => {
21346
+ if (node.type.name === nodeName && node.attrs.uid === uid) {
21347
+ targetPos = pos;
21348
+ targetSize = node.nodeSize;
21349
+ return false;
21522
21350
  }
21351
+ });
21352
+ if (targetPos !== null && targetSize !== null) {
21353
+ return commands.deleteRange({
21354
+ from: targetPos,
21355
+ to: Number(targetPos) + Number(targetSize)
21356
+ });
21523
21357
  }
21524
- return new RemoveNodeMarkStep(this.pos, this.mark);
21525
- }
21526
- map(mapping) {
21527
- let pos = mapping.mapResult(this.pos, 1);
21528
- return pos.deletedAfter ? null : new AddNodeMarkStep(pos.pos, this.mark);
21529
- }
21530
- toJSON() {
21531
- return { stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON() };
21532
- }
21533
- /**
21534
- @internal
21535
- */
21536
- static fromJSON(schema, json) {
21537
- if (typeof json.pos != "number")
21538
- throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON");
21539
- return new AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
21540
- }
21541
- }
21542
- Step.jsonID("addNodeMark", AddNodeMarkStep);
21543
- class RemoveNodeMarkStep extends Step {
21544
- /**
21545
- Create a mark-removing step.
21546
- */
21547
- constructor(pos, mark) {
21548
- super();
21549
- this.pos = pos;
21550
- this.mark = mark;
21551
- }
21552
- apply(doc) {
21553
- let node = doc.nodeAt(this.pos);
21554
- if (!node)
21555
- return StepResult.fail("No node at mark step's position");
21556
- let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
21557
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
21558
- }
21559
- invert(doc) {
21560
- let node = doc.nodeAt(this.pos);
21561
- if (!node || !this.mark.isInSet(node.marks))
21562
- return this;
21563
- return new AddNodeMarkStep(this.pos, this.mark);
21564
- }
21565
- map(mapping) {
21566
- let pos = mapping.mapResult(this.pos, 1);
21567
- return pos.deletedAfter ? null : new RemoveNodeMarkStep(pos.pos, this.mark);
21568
- }
21569
- toJSON() {
21570
- return { stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON() };
21571
- }
21572
- /**
21573
- @internal
21574
- */
21575
- static fromJSON(schema, json) {
21576
- if (typeof json.pos != "number")
21577
- throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON");
21578
- return new RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
21579
- }
21580
- }
21581
- Step.jsonID("removeNodeMark", RemoveNodeMarkStep);
21582
- class ReplaceStep extends Step {
21583
- /**
21584
- The given `slice` should fit the 'gap' between `from` and
21585
- `to`—the depths must line up, and the surrounding nodes must be
21586
- able to be joined with the open sides of the slice. When
21587
- `structure` is true, the step will fail if the content between
21588
- from and to is not just a sequence of closing and then opening
21589
- tokens (this is to guard against rebased replace steps
21590
- overwriting something they weren't supposed to).
21591
- */
21592
- constructor(from, to, slice, structure = false) {
21593
- super();
21594
- this.from = from;
21595
- this.to = to;
21596
- this.slice = slice;
21597
- this.structure = structure;
21598
- }
21599
- apply(doc) {
21600
- if (this.structure && contentBetween(doc, this.from, this.to))
21601
- return StepResult.fail("Structure replace would overwrite content");
21602
- return StepResult.fromReplace(doc, this.from, this.to, this.slice);
21603
- }
21604
- getMap() {
21605
- return new StepMap([this.from, this.to - this.from, this.slice.size]);
21606
- }
21607
- invert(doc) {
21608
- return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to));
21609
- }
21610
- map(mapping) {
21611
- let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21612
- if (from.deletedAcross && to.deletedAcross)
21613
- return null;
21614
- return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
21615
- }
21616
- merge(other) {
21617
- if (!(other instanceof ReplaceStep) || other.structure || this.structure)
21618
- return null;
21619
- if (this.from + this.slice.size == other.from && !this.slice.openEnd && !other.slice.openStart) {
21620
- let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(this.slice.content.append(other.slice.content), this.slice.openStart, other.slice.openEnd);
21621
- return new ReplaceStep(this.from, this.to + (other.to - other.from), slice, this.structure);
21622
- } else if (other.to == this.from && !this.slice.openStart && !other.slice.openEnd) {
21623
- let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(other.slice.content.append(this.slice.content), other.slice.openStart, this.slice.openEnd);
21624
- return new ReplaceStep(other.from, this.to, slice, this.structure);
21625
- } else {
21626
- return null;
21358
+ return false;
21359
+ };
21360
+ };
21361
+ const createUpdateByUidCommand = (nodeName) => {
21362
+ return (uid, attrs) => ({ state: state2, dispatch }) => {
21363
+ if (!dispatch) return false;
21364
+ const { doc, tr } = state2;
21365
+ let found2 = false;
21366
+ doc.descendants((node, pos) => {
21367
+ if (node.type.name === nodeName && node.attrs.uid === uid) {
21368
+ tr.setNodeMarkup(pos, void 0, {
21369
+ ...node.attrs,
21370
+ ...attrs
21371
+ });
21372
+ found2 = true;
21373
+ return false;
21374
+ }
21375
+ });
21376
+ if (found2) {
21377
+ dispatch(tr);
21378
+ return true;
21379
+ }
21380
+ return false;
21381
+ };
21382
+ };
21383
+ const createDeleteMarkByUidCommand = (markName) => {
21384
+ return (uid) => ({
21385
+ state: state2,
21386
+ dispatch
21387
+ }) => {
21388
+ if (!dispatch) return false;
21389
+ const { doc, schema } = state2;
21390
+ let tr = state2.tr;
21391
+ let found2 = false;
21392
+ const positions = [];
21393
+ doc.descendants((node, pos) => {
21394
+ if (node.isText && node.marks?.length) {
21395
+ node.marks.forEach((mark) => {
21396
+ if (mark.type.name === markName && mark.attrs.uid === uid) {
21397
+ positions.push({
21398
+ from: pos,
21399
+ to: pos + node.nodeSize
21400
+ });
21401
+ found2 = true;
21402
+ }
21403
+ });
21404
+ }
21405
+ });
21406
+ if (found2) {
21407
+ const markType = schema.marks[markName];
21408
+ if (markType) {
21409
+ positions.forEach(({ from, to }) => {
21410
+ tr = tr.removeMark(from, to, markType);
21411
+ });
21412
+ dispatch(tr);
21413
+ }
21414
+ }
21415
+ return found2;
21416
+ };
21417
+ };
21418
+ const createUpdateMarkByUidCommand = (markName) => {
21419
+ return (uid, attrs) => ({
21420
+ state: state2,
21421
+ dispatch
21422
+ }) => {
21423
+ console.log("=== UPDATE MARK COMMAND START ===");
21424
+ console.log("markName:", markName);
21425
+ console.log("uid:", uid);
21426
+ console.log("attrs:", attrs);
21427
+ console.log("state.doc:", state2.doc.toJSON());
21428
+ if (!dispatch) {
21429
+ console.log("No dispatch, returning false");
21430
+ return false;
21431
+ }
21432
+ const { doc, schema } = state2;
21433
+ let tr = state2.tr;
21434
+ let found2 = false;
21435
+ const positions = [];
21436
+ doc.descendants((node, pos) => {
21437
+ if (node.isText && node.marks?.length) {
21438
+ node.marks.forEach((mark) => {
21439
+ console.log("Checking mark:", {
21440
+ markType: mark.type.name,
21441
+ markUid: mark.attrs.uid,
21442
+ targetMarkName: markName,
21443
+ targetUid: uid,
21444
+ matches: mark.type.name === markName && mark.attrs.uid === uid
21445
+ });
21446
+ if (mark.type.name === markName && mark.attrs.uid === uid) {
21447
+ positions.push({
21448
+ from: pos,
21449
+ to: pos + node.nodeSize,
21450
+ mark
21451
+ });
21452
+ found2 = true;
21453
+ console.log("Found matching mark at:", { from: pos, to: pos + node.nodeSize });
21454
+ }
21455
+ });
21456
+ }
21457
+ });
21458
+ console.log("Total positions found:", positions.length);
21459
+ console.log("Positions:", positions);
21460
+ if (found2) {
21461
+ positions.forEach(({ from, to, mark }) => {
21462
+ const newAttrs = { ...mark.attrs, ...attrs };
21463
+ const markType = schema.marks[markName];
21464
+ console.log("Applying update:", { from, to, oldAttrs: mark.attrs, newAttrs });
21465
+ if (markType) {
21466
+ console.log("Before removeMark - tr.doc:", tr.doc.toJSON());
21467
+ tr = tr.removeMark(from, to, markType);
21468
+ console.log("After removeMark - tr.doc:", tr.doc.toJSON());
21469
+ tr = tr.addMark(from, to, markType.create(newAttrs));
21470
+ console.log("After addMark - tr.doc:", tr.doc.toJSON());
21471
+ }
21472
+ });
21473
+ console.log("About to dispatch transaction");
21474
+ console.log("Final tr.doc:", tr.doc.toJSON());
21475
+ console.log("Original state.doc:", state2.doc.toJSON());
21476
+ dispatch(tr);
21477
+ console.log("Transaction dispatched successfully");
21627
21478
  }
21628
- }
21629
- toJSON() {
21630
- let json = { stepType: "replace", from: this.from, to: this.to };
21631
- if (this.slice.size)
21632
- json.slice = this.slice.toJSON();
21633
- if (this.structure)
21634
- json.structure = true;
21635
- return json;
21636
- }
21637
- /**
21638
- @internal
21639
- */
21640
- static fromJSON(schema, json) {
21641
- if (typeof json.from != "number" || typeof json.to != "number")
21642
- throw new RangeError("Invalid input for ReplaceStep.fromJSON");
21643
- return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure);
21644
- }
21645
- }
21646
- Step.jsonID("replace", ReplaceStep);
21647
- class ReplaceAroundStep extends Step {
21648
- /**
21649
- Create a replace-around step with the given range and gap.
21650
- `insert` should be the point in the slice into which the content
21651
- of the gap should be moved. `structure` has the same meaning as
21652
- it has in the [`ReplaceStep`](https://prosemirror.net/docs/ref/#transform.ReplaceStep) class.
21653
- */
21654
- constructor(from, to, gapFrom, gapTo, slice, insert, structure = false) {
21655
- super();
21656
- this.from = from;
21657
- this.to = to;
21658
- this.gapFrom = gapFrom;
21659
- this.gapTo = gapTo;
21660
- this.slice = slice;
21661
- this.insert = insert;
21662
- this.structure = structure;
21663
- }
21664
- apply(doc) {
21665
- if (this.structure && (contentBetween(doc, this.from, this.gapFrom) || contentBetween(doc, this.gapTo, this.to)))
21666
- return StepResult.fail("Structure gap-replace would overwrite content");
21667
- let gap = doc.slice(this.gapFrom, this.gapTo);
21668
- if (gap.openStart || gap.openEnd)
21669
- return StepResult.fail("Gap is not a flat range");
21670
- let inserted = this.slice.insertAt(this.insert, gap.content);
21671
- if (!inserted)
21672
- return StepResult.fail("Content does not fit in gap");
21673
- return StepResult.fromReplace(doc, this.from, this.to, inserted);
21674
- }
21675
- getMap() {
21676
- return new StepMap([
21677
- this.from,
21678
- this.gapFrom - this.from,
21679
- this.insert,
21680
- this.gapTo,
21681
- this.to - this.gapTo,
21682
- this.slice.size - this.insert
21683
- ]);
21684
- }
21685
- invert(doc) {
21686
- let gap = this.gapTo - this.gapFrom;
21687
- return new ReplaceAroundStep(this.from, this.from + this.slice.size + gap, this.from + this.insert, this.from + this.insert + gap, doc.slice(this.from, this.to).removeBetween(this.gapFrom - this.from, this.gapTo - this.from), this.gapFrom - this.from, this.structure);
21688
- }
21689
- map(mapping) {
21690
- let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21691
- let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1);
21692
- let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1);
21693
- if (from.deletedAcross && to.deletedAcross || gapFrom < from.pos || gapTo > to.pos)
21694
- return null;
21695
- return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
21696
- }
21697
- toJSON() {
21698
- let json = {
21699
- stepType: "replaceAround",
21700
- from: this.from,
21701
- to: this.to,
21702
- gapFrom: this.gapFrom,
21703
- gapTo: this.gapTo,
21704
- insert: this.insert
21479
+ console.log("=== UPDATE MARK COMMAND END ===");
21480
+ return found2;
21481
+ };
21482
+ };
21483
+ const DEFAULT_MEDIA_ATTRS = {
21484
+ uid: "",
21485
+ src: "",
21486
+ type: MediaNodeTypes.Image,
21487
+ style: "object-fit: contain; width: 100%; border-radius: 0.5rem;",
21488
+ maxWidth: MEDIA_MIN_WIDTH,
21489
+ align: Alignment.LEFT,
21490
+ caption: "",
21491
+ uploading: false,
21492
+ progress: 0
21493
+ };
21494
+ const MediaNode = core.Node.create({
21495
+ name: "media",
21496
+ group: "block",
21497
+ atom: true,
21498
+ addAttributes() {
21499
+ return {
21500
+ uid: { default: DEFAULT_MEDIA_ATTRS.uid },
21501
+ src: { default: DEFAULT_MEDIA_ATTRS.src },
21502
+ type: { default: DEFAULT_MEDIA_ATTRS.type },
21503
+ style: { default: DEFAULT_MEDIA_ATTRS.style },
21504
+ maxWidth: { default: DEFAULT_MEDIA_ATTRS.maxWidth },
21505
+ align: { default: DEFAULT_MEDIA_ATTRS.align },
21506
+ caption: { default: DEFAULT_MEDIA_ATTRS.caption },
21507
+ uploading: { default: DEFAULT_MEDIA_ATTRS.uploading },
21508
+ progress: { default: DEFAULT_MEDIA_ATTRS.progress },
21509
+ payload: { default: null, parseHTML: () => null, renderHTML: () => ({}) }
21510
+ };
21511
+ },
21512
+ draggable() {
21513
+ return Boolean(!this?.options?.attrs?.uploading && !this?.options?.attrs?.mediaMode);
21514
+ },
21515
+ selectable() {
21516
+ return Boolean(!this?.options?.attrs?.mediaMode);
21517
+ },
21518
+ parseHTML() {
21519
+ return [
21520
+ {
21521
+ tag: "div[data-media]",
21522
+ getAttrs: (dom) => {
21523
+ return {
21524
+ uid: dom.getAttribute("data-uid") || "",
21525
+ src: dom.getAttribute("data-src") || "",
21526
+ type: dom.getAttribute("data-type") || MediaNodeTypes.Image,
21527
+ align: dom.getAttribute("data-align") || Alignment.LEFT,
21528
+ caption: dom.getAttribute("data-caption") || "",
21529
+ style: dom.getAttribute("style") || DEFAULT_MEDIA_ATTRS.style
21530
+ };
21531
+ }
21532
+ }
21533
+ ];
21534
+ },
21535
+ renderHTML({ HTMLAttributes }) {
21536
+ const { uid, src, style, type, align, maxWidth, caption } = HTMLAttributes;
21537
+ const tag = MEDIA_TAGS[type];
21538
+ const wrapperAttrs = core.mergeAttributes({
21539
+ style: buildWrapperStyle(align),
21540
+ "data-media": true,
21541
+ "data-uid": uid,
21542
+ "data-type": type,
21543
+ "data-src": src,
21544
+ "data-caption": caption || "",
21545
+ "data-align": align
21546
+ });
21547
+ const mediaAttrs = core.mergeAttributes(
21548
+ { src, style: buildMediaStyle(style, type, maxWidth) },
21549
+ { controls: type === MediaNodeTypes.Audio || type === MediaNodeTypes.Video }
21550
+ );
21551
+ return ["div", wrapperAttrs, [tag, mediaAttrs]];
21552
+ },
21553
+ addNodeView() {
21554
+ return react.ReactNodeViewRenderer(MediaNodeView);
21555
+ },
21556
+ addCommands() {
21557
+ return {
21558
+ insertMedia: (options) => ({ commands, chain }) => {
21559
+ if (this?.options?.attrs?.mediaMode) {
21560
+ return chain().insertContent([{ type: this.name, attrs: options }]).command(({ tr, dispatch }) => {
21561
+ if (dispatch) {
21562
+ const endPos = tr.doc.content.size;
21563
+ const $pos = tr.doc.resolve(endPos);
21564
+ tr.setSelection(state.TextSelection.near($pos));
21565
+ }
21566
+ return true;
21567
+ }).run();
21568
+ }
21569
+ return commands.insertContent([
21570
+ { type: this.name, attrs: options },
21571
+ { type: "paragraph" }
21572
+ //! Fixes multiple media overlapping issue (But removes default selection feature)
21573
+ ]);
21574
+ },
21575
+ updateMediaByUid: createUpdateByUidCommand(this.name),
21576
+ deleteMediaByUid: createDeleteByUidCommand(this.name),
21577
+ updateMediaProgress: (uid, progress) => ({ commands }) => {
21578
+ return commands.updateMediaByUid(uid, { progress });
21579
+ }
21580
+ };
21581
+ },
21582
+ addKeyboardShortcuts() {
21583
+ return {
21584
+ Backspace: () => {
21585
+ const { selection, doc } = this.editor.state;
21586
+ const $pos = selection.$anchor;
21587
+ const nodeBefore = $pos.nodeBefore;
21588
+ if (nodeBefore?.type.name === this.name && nodeBefore.attrs.uploading) {
21589
+ toast.info("Please wait for upload to complete or cancel it first");
21590
+ return true;
21591
+ }
21592
+ const nodeAtSelection = doc.nodeAt(selection.from);
21593
+ if (nodeAtSelection?.type.name === this.name && nodeAtSelection.attrs.uploading) {
21594
+ toast.info("Please wait for upload to complete or cancel it first");
21595
+ return true;
21596
+ }
21597
+ return false;
21598
+ },
21599
+ Delete: () => {
21600
+ const { selection, doc } = this.editor.state;
21601
+ const $pos = selection.$anchor;
21602
+ const nodeAfter = $pos.nodeAfter;
21603
+ if (nodeAfter?.type.name === this.name && nodeAfter.attrs.uploading) {
21604
+ toast.info("Please wait for upload to complete or cancel it first");
21605
+ return true;
21606
+ }
21607
+ const nodeAtSelection = doc.nodeAt(selection.from);
21608
+ if (nodeAtSelection?.type.name === this.name && nodeAtSelection.attrs.uploading) {
21609
+ toast.info("Please wait for upload to complete or cancel it first");
21610
+ return true;
21611
+ }
21612
+ return false;
21613
+ }
21705
21614
  };
21706
- if (this.slice.size)
21707
- json.slice = this.slice.toJSON();
21708
- if (this.structure)
21709
- json.structure = true;
21710
- return json;
21711
- }
21712
- /**
21713
- @internal
21714
- */
21715
- static fromJSON(schema, json) {
21716
- if (typeof json.from != "number" || typeof json.to != "number" || typeof json.gapFrom != "number" || typeof json.gapTo != "number" || typeof json.insert != "number")
21717
- throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");
21718
- return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo, Slice.fromJSON(schema, json.slice), json.insert, !!json.structure);
21719
21615
  }
21616
+ });
21617
+ const buildWrapperStyle = (align) => `width: 100%; display: flex; justify-content: ${getAlignmentStyle(align)}; margin:16px 0`;
21618
+ const buildMediaStyle = (baseStyle, type, maxWidth) => `${baseStyle}; max-width: ${type === MediaNodeTypes.Audio ? "100%" : `${maxWidth}%`}; ${type !== MediaNodeTypes.Audio ? "height: auto;" : ""}`;
21619
+ const getAlignmentStyle = (align) => {
21620
+ const map = {
21621
+ [Alignment.LEFT]: "flex-start",
21622
+ [Alignment.CENTER]: "center",
21623
+ [Alignment.RIGHT]: "flex-end"
21624
+ };
21625
+ return map[align] || "flex-start";
21626
+ };
21627
+ const lower16 = 65535;
21628
+ const factor16 = Math.pow(2, 16);
21629
+ function makeRecover(index2, offset2) {
21630
+ return index2 + offset2 * factor16;
21720
21631
  }
21721
- Step.jsonID("replaceAround", ReplaceAroundStep);
21722
- function contentBetween(doc, from, to) {
21723
- let $from = doc.resolve(from), dist = to - from, depth = $from.depth;
21724
- while (dist > 0 && depth > 0 && $from.indexAfter(depth) == $from.node(depth).childCount) {
21725
- depth--;
21726
- dist--;
21727
- }
21728
- if (dist > 0) {
21729
- let next = $from.node(depth).maybeChild($from.indexAfter(depth));
21730
- while (dist > 0) {
21731
- if (!next || next.isLeaf)
21732
- return true;
21733
- next = next.firstChild;
21734
- dist--;
21735
- }
21736
- }
21737
- return false;
21632
+ function recoverIndex(value) {
21633
+ return value & lower16;
21738
21634
  }
21739
- class AttrStep extends Step {
21635
+ function recoverOffset(value) {
21636
+ return (value - (value & lower16)) / factor16;
21637
+ }
21638
+ const DEL_BEFORE = 1, DEL_AFTER = 2, DEL_ACROSS = 4, DEL_SIDE = 8;
21639
+ class MapResult {
21740
21640
  /**
21741
- Construct an attribute step.
21641
+ @internal
21742
21642
  */
21743
- constructor(pos, attr, value) {
21744
- super();
21643
+ constructor(pos, delInfo, recover) {
21745
21644
  this.pos = pos;
21746
- this.attr = attr;
21747
- this.value = value;
21748
- }
21749
- apply(doc) {
21750
- let node = doc.nodeAt(this.pos);
21751
- if (!node)
21752
- return StepResult.fail("No node at attribute step's position");
21753
- let attrs = /* @__PURE__ */ Object.create(null);
21754
- for (let name in node.attrs)
21755
- attrs[name] = node.attrs[name];
21756
- attrs[this.attr] = this.value;
21757
- let updated = node.type.create(attrs, null, node.marks);
21758
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
21759
- }
21760
- getMap() {
21761
- return StepMap.empty;
21645
+ this.delInfo = delInfo;
21646
+ this.recover = recover;
21762
21647
  }
21763
- invert(doc) {
21764
- return new AttrStep(this.pos, this.attr, doc.nodeAt(this.pos).attrs[this.attr]);
21648
+ /**
21649
+ Tells you whether the position was deleted, that is, whether the
21650
+ step removed the token on the side queried (via the `assoc`)
21651
+ argument from the document.
21652
+ */
21653
+ get deleted() {
21654
+ return (this.delInfo & DEL_SIDE) > 0;
21765
21655
  }
21766
- map(mapping) {
21767
- let pos = mapping.mapResult(this.pos, 1);
21768
- return pos.deletedAfter ? null : new AttrStep(pos.pos, this.attr, this.value);
21656
+ /**
21657
+ Tells you whether the token before the mapped position was deleted.
21658
+ */
21659
+ get deletedBefore() {
21660
+ return (this.delInfo & (DEL_BEFORE | DEL_ACROSS)) > 0;
21769
21661
  }
21770
- toJSON() {
21771
- return { stepType: "attr", pos: this.pos, attr: this.attr, value: this.value };
21662
+ /**
21663
+ True when the token after the mapped position was deleted.
21664
+ */
21665
+ get deletedAfter() {
21666
+ return (this.delInfo & (DEL_AFTER | DEL_ACROSS)) > 0;
21772
21667
  }
21773
- static fromJSON(schema, json) {
21774
- if (typeof json.pos != "number" || typeof json.attr != "string")
21775
- throw new RangeError("Invalid input for AttrStep.fromJSON");
21776
- return new AttrStep(json.pos, json.attr, json.value);
21668
+ /**
21669
+ Tells whether any of the steps mapped through deletes across the
21670
+ position (including both the token before and after the
21671
+ position).
21672
+ */
21673
+ get deletedAcross() {
21674
+ return (this.delInfo & DEL_ACROSS) > 0;
21777
21675
  }
21778
21676
  }
21779
- Step.jsonID("attr", AttrStep);
21780
- class DocAttrStep extends Step {
21677
+ class StepMap {
21781
21678
  /**
21782
- Construct an attribute step.
21679
+ Create a position map. The modifications to the document are
21680
+ represented as an array of numbers, in which each group of three
21681
+ represents a modified chunk as `[start, oldSize, newSize]`.
21783
21682
  */
21784
- constructor(attr, value) {
21785
- super();
21786
- this.attr = attr;
21787
- this.value = value;
21788
- }
21789
- apply(doc) {
21790
- let attrs = /* @__PURE__ */ Object.create(null);
21791
- for (let name in doc.attrs)
21792
- attrs[name] = doc.attrs[name];
21793
- attrs[this.attr] = this.value;
21794
- let updated = doc.type.create(attrs, doc.content, doc.marks);
21795
- return StepResult.ok(updated);
21683
+ constructor(ranges, inverted = false) {
21684
+ this.ranges = ranges;
21685
+ this.inverted = inverted;
21686
+ if (!ranges.length && StepMap.empty)
21687
+ return StepMap.empty;
21796
21688
  }
21797
- getMap() {
21798
- return StepMap.empty;
21689
+ /**
21690
+ @internal
21691
+ */
21692
+ recover(value) {
21693
+ let diff = 0, index2 = recoverIndex(value);
21694
+ if (!this.inverted)
21695
+ for (let i = 0; i < index2; i++)
21696
+ diff += this.ranges[i * 3 + 2] - this.ranges[i * 3 + 1];
21697
+ return this.ranges[index2 * 3] + diff + recoverOffset(value);
21799
21698
  }
21800
- invert(doc) {
21801
- return new DocAttrStep(this.attr, doc.attrs[this.attr]);
21699
+ mapResult(pos, assoc = 1) {
21700
+ return this._map(pos, assoc, false);
21802
21701
  }
21803
- map(mapping) {
21804
- return this;
21702
+ map(pos, assoc = 1) {
21703
+ return this._map(pos, assoc, true);
21805
21704
  }
21806
- toJSON() {
21807
- return { stepType: "docAttr", attr: this.attr, value: this.value };
21705
+ /**
21706
+ @internal
21707
+ */
21708
+ _map(pos, assoc, simple) {
21709
+ let diff = 0, oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21710
+ for (let i = 0; i < this.ranges.length; i += 3) {
21711
+ let start2 = this.ranges[i] - (this.inverted ? diff : 0);
21712
+ if (start2 > pos)
21713
+ break;
21714
+ let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex], end2 = start2 + oldSize;
21715
+ if (pos <= end2) {
21716
+ let side = !oldSize ? assoc : pos == start2 ? -1 : pos == end2 ? 1 : assoc;
21717
+ let result = start2 + diff + (side < 0 ? 0 : newSize);
21718
+ if (simple)
21719
+ return result;
21720
+ let recover = pos == (assoc < 0 ? start2 : end2) ? null : makeRecover(i / 3, pos - start2);
21721
+ let del = pos == start2 ? DEL_AFTER : pos == end2 ? DEL_BEFORE : DEL_ACROSS;
21722
+ if (assoc < 0 ? pos != start2 : pos != end2)
21723
+ del |= DEL_SIDE;
21724
+ return new MapResult(result, del, recover);
21725
+ }
21726
+ diff += newSize - oldSize;
21727
+ }
21728
+ return simple ? pos + diff : new MapResult(pos + diff, 0, null);
21808
21729
  }
21809
- static fromJSON(schema, json) {
21810
- if (typeof json.attr != "string")
21811
- throw new RangeError("Invalid input for DocAttrStep.fromJSON");
21812
- return new DocAttrStep(json.attr, json.value);
21730
+ /**
21731
+ @internal
21732
+ */
21733
+ touches(pos, recover) {
21734
+ let diff = 0, index2 = recoverIndex(recover);
21735
+ let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21736
+ for (let i = 0; i < this.ranges.length; i += 3) {
21737
+ let start2 = this.ranges[i] - (this.inverted ? diff : 0);
21738
+ if (start2 > pos)
21739
+ break;
21740
+ let oldSize = this.ranges[i + oldIndex], end2 = start2 + oldSize;
21741
+ if (pos <= end2 && i == index2 * 3)
21742
+ return true;
21743
+ diff += this.ranges[i + newIndex] - oldSize;
21744
+ }
21745
+ return false;
21813
21746
  }
21814
- }
21815
- Step.jsonID("docAttr", DocAttrStep);
21816
- let TransformError = class extends Error {
21817
- };
21818
- TransformError = function TransformError2(message) {
21819
- let err = Error.call(this, message);
21820
- err.__proto__ = TransformError2.prototype;
21821
- return err;
21822
- };
21823
- TransformError.prototype = Object.create(Error.prototype);
21824
- TransformError.prototype.constructor = TransformError;
21825
- TransformError.prototype.name = "TransformError";
21826
- const classesById = /* @__PURE__ */ Object.create(null);
21827
- class Selection {
21828
21747
  /**
21829
- Initialize a selection with the head and anchor and ranges. If no
21830
- ranges are given, constructs a single range across `$anchor` and
21831
- `$head`.
21748
+ Calls the given function on each of the changed ranges included in
21749
+ this map.
21832
21750
  */
21833
- constructor($anchor, $head, ranges) {
21834
- this.$anchor = $anchor;
21835
- this.$head = $head;
21836
- this.ranges = ranges || [new SelectionRange($anchor.min($head), $anchor.max($head))];
21751
+ forEach(f) {
21752
+ let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2;
21753
+ for (let i = 0, diff = 0; i < this.ranges.length; i += 3) {
21754
+ let start2 = this.ranges[i], oldStart = start2 - (this.inverted ? diff : 0), newStart = start2 + (this.inverted ? 0 : diff);
21755
+ let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex];
21756
+ f(oldStart, oldStart + oldSize, newStart, newStart + newSize);
21757
+ diff += newSize - oldSize;
21758
+ }
21837
21759
  }
21838
21760
  /**
21839
- The selection's anchor, as an unresolved position.
21761
+ Create an inverted version of this map. The result can be used to
21762
+ map positions in the post-step document to the pre-step document.
21840
21763
  */
21841
- get anchor() {
21842
- return this.$anchor.pos;
21764
+ invert() {
21765
+ return new StepMap(this.ranges, !this.inverted);
21843
21766
  }
21844
21767
  /**
21845
- The selection's head.
21768
+ @internal
21846
21769
  */
21847
- get head() {
21848
- return this.$head.pos;
21770
+ toString() {
21771
+ return (this.inverted ? "-" : "") + JSON.stringify(this.ranges);
21849
21772
  }
21850
21773
  /**
21851
- The lower bound of the selection's main range.
21774
+ Create a map that moves all positions by offset `n` (which may be
21775
+ negative). This can be useful when applying steps meant for a
21776
+ sub-document to a larger document, or vice-versa.
21852
21777
  */
21853
- get from() {
21854
- return this.$from.pos;
21778
+ static offset(n) {
21779
+ return n == 0 ? StepMap.empty : new StepMap(n < 0 ? [0, -n, 0] : [0, 0, n]);
21855
21780
  }
21781
+ }
21782
+ StepMap.empty = new StepMap([]);
21783
+ const stepsByID = /* @__PURE__ */ Object.create(null);
21784
+ class Step {
21856
21785
  /**
21857
- The upper bound of the selection's main range.
21786
+ Get the step map that represents the changes made by this step,
21787
+ and which can be used to transform between positions in the old
21788
+ and the new document.
21858
21789
  */
21859
- get to() {
21860
- return this.$to.pos;
21790
+ getMap() {
21791
+ return StepMap.empty;
21861
21792
  }
21862
21793
  /**
21863
- The resolved lower bound of the selection's main range.
21794
+ Try to merge this step with another one, to be applied directly
21795
+ after it. Returns the merged step when possible, null if the
21796
+ steps can't be merged.
21864
21797
  */
21865
- get $from() {
21866
- return this.ranges[0].$from;
21798
+ merge(other) {
21799
+ return null;
21867
21800
  }
21868
21801
  /**
21869
- The resolved upper bound of the selection's main range.
21802
+ Deserialize a step from its JSON representation. Will call
21803
+ through to the step class' own implementation of this method.
21870
21804
  */
21871
- get $to() {
21872
- return this.ranges[0].$to;
21805
+ static fromJSON(schema, json) {
21806
+ if (!json || !json.stepType)
21807
+ throw new RangeError("Invalid input for Step.fromJSON");
21808
+ let type = stepsByID[json.stepType];
21809
+ if (!type)
21810
+ throw new RangeError(`No step type ${json.stepType} defined`);
21811
+ return type.fromJSON(schema, json);
21873
21812
  }
21874
21813
  /**
21875
- Indicates whether the selection contains any content.
21814
+ To be able to serialize steps to JSON, each step needs a string
21815
+ ID to attach to its JSON representation. Use this method to
21816
+ register an ID for your step classes. Try to pick something
21817
+ that's unlikely to clash with steps from other modules.
21876
21818
  */
21877
- get empty() {
21878
- let ranges = this.ranges;
21879
- for (let i = 0; i < ranges.length; i++)
21880
- if (ranges[i].$from.pos != ranges[i].$to.pos)
21881
- return false;
21882
- return true;
21819
+ static jsonID(id, stepClass) {
21820
+ if (id in stepsByID)
21821
+ throw new RangeError("Duplicate use of step JSON ID " + id);
21822
+ stepsByID[id] = stepClass;
21823
+ stepClass.prototype.jsonID = id;
21824
+ return stepClass;
21883
21825
  }
21826
+ }
21827
+ class StepResult {
21884
21828
  /**
21885
- Get the content of this selection as a slice.
21829
+ @internal
21886
21830
  */
21887
- content() {
21888
- return this.$from.doc.slice(this.from, this.to, true);
21831
+ constructor(doc, failed) {
21832
+ this.doc = doc;
21833
+ this.failed = failed;
21889
21834
  }
21890
21835
  /**
21891
- Replace the selection with a slice or, if no slice is given,
21892
- delete the selection. Will append to the given transaction.
21836
+ Create a successful step result.
21893
21837
  */
21894
- replace(tr, content = Slice.empty) {
21895
- let lastNode = content.content.lastChild, lastParent = null;
21896
- for (let i = 0; i < content.openEnd; i++) {
21897
- lastParent = lastNode;
21898
- lastNode = lastNode.lastChild;
21899
- }
21900
- let mapFrom = tr.steps.length, ranges = this.ranges;
21901
- for (let i = 0; i < ranges.length; i++) {
21902
- let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
21903
- tr.replaceRange(mapping.map($from.pos), mapping.map($to.pos), i ? Slice.empty : content);
21904
- if (i == 0)
21905
- selectionToInsertionEnd(tr, mapFrom, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1);
21906
- }
21838
+ static ok(doc) {
21839
+ return new StepResult(doc, null);
21907
21840
  }
21908
21841
  /**
21909
- Replace the selection with the given node, appending the changes
21910
- to the given transaction.
21842
+ Create a failed step result.
21911
21843
  */
21912
- replaceWith(tr, node) {
21913
- let mapFrom = tr.steps.length, ranges = this.ranges;
21914
- for (let i = 0; i < ranges.length; i++) {
21915
- let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
21916
- let from = mapping.map($from.pos), to = mapping.map($to.pos);
21917
- if (i) {
21918
- tr.deleteRange(from, to);
21919
- } else {
21920
- tr.replaceRangeWith(from, to, node);
21921
- selectionToInsertionEnd(tr, mapFrom, node.isInline ? -1 : 1);
21922
- }
21923
- }
21844
+ static fail(message) {
21845
+ return new StepResult(null, message);
21924
21846
  }
21925
21847
  /**
21926
- Find a valid cursor or leaf node selection starting at the given
21927
- position and searching back if `dir` is negative, and forward if
21928
- positive. When `textOnly` is true, only consider cursor
21929
- selections. Will return null when no valid selection position is
21930
- found.
21848
+ Call [`Node.replace`](https://prosemirror.net/docs/ref/#model.Node.replace) with the given
21849
+ arguments. Create a successful result if it succeeds, and a
21850
+ failed one if it throws a `ReplaceError`.
21931
21851
  */
21932
- static findFrom($pos, dir, textOnly = false) {
21933
- let inner = $pos.parent.inlineContent ? new TextSelection($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
21934
- if (inner)
21935
- return inner;
21936
- for (let depth = $pos.depth - 1; depth >= 0; depth--) {
21937
- let found2 = dir < 0 ? findSelectionIn($pos.node(0), $pos.node(depth), $pos.before(depth + 1), $pos.index(depth), dir, textOnly) : findSelectionIn($pos.node(0), $pos.node(depth), $pos.after(depth + 1), $pos.index(depth) + 1, dir, textOnly);
21938
- if (found2)
21939
- return found2;
21852
+ static fromReplace(doc, from, to, slice) {
21853
+ try {
21854
+ return StepResult.ok(doc.replace(from, to, slice));
21855
+ } catch (e) {
21856
+ if (e instanceof ReplaceError)
21857
+ return StepResult.fail(e.message);
21858
+ throw e;
21940
21859
  }
21941
- return null;
21942
21860
  }
21943
- /**
21944
- Find a valid cursor or leaf node selection near the given
21945
- position. Searches forward first by default, but if `bias` is
21946
- negative, it will search backwards first.
21947
- */
21948
- static near($pos, bias = 1) {
21949
- return this.findFrom($pos, bias) || this.findFrom($pos, -bias) || new AllSelection($pos.node(0));
21861
+ }
21862
+ function mapFragment(fragment, f, parent2) {
21863
+ let mapped = [];
21864
+ for (let i = 0; i < fragment.childCount; i++) {
21865
+ let child = fragment.child(i);
21866
+ if (child.content.size)
21867
+ child = child.copy(mapFragment(child.content, f, child));
21868
+ if (child.isInline)
21869
+ child = f(child, parent2, i);
21870
+ mapped.push(child);
21950
21871
  }
21872
+ return Fragment.fromArray(mapped);
21873
+ }
21874
+ class AddMarkStep extends Step {
21951
21875
  /**
21952
- Find the cursor or leaf node selection closest to the start of
21953
- the given document. Will return an
21954
- [`AllSelection`](https://prosemirror.net/docs/ref/#state.AllSelection) if no valid position
21955
- exists.
21876
+ Create a mark step.
21956
21877
  */
21957
- static atStart(doc) {
21958
- return findSelectionIn(doc, doc, 0, 0, 1) || new AllSelection(doc);
21878
+ constructor(from, to, mark) {
21879
+ super();
21880
+ this.from = from;
21881
+ this.to = to;
21882
+ this.mark = mark;
21959
21883
  }
21960
- /**
21961
- Find the cursor or leaf node selection closest to the end of the
21962
- given document.
21963
- */
21964
- static atEnd(doc) {
21965
- return findSelectionIn(doc, doc, doc.content.size, doc.childCount, -1) || new AllSelection(doc);
21884
+ apply(doc) {
21885
+ let oldSlice = doc.slice(this.from, this.to), $from = doc.resolve(this.from);
21886
+ let parent2 = $from.node($from.sharedDepth(this.to));
21887
+ let slice = new Slice(mapFragment(oldSlice.content, (node, parent3) => {
21888
+ if (!node.isAtom || !parent3.type.allowsMarkType(this.mark.type))
21889
+ return node;
21890
+ return node.mark(this.mark.addToSet(node.marks));
21891
+ }, parent2), oldSlice.openStart, oldSlice.openEnd);
21892
+ return StepResult.fromReplace(doc, this.from, this.to, slice);
21966
21893
  }
21967
- /**
21968
- Deserialize the JSON representation of a selection. Must be
21969
- implemented for custom classes (as a static class method).
21970
- */
21971
- static fromJSON(doc, json) {
21972
- if (!json || !json.type)
21973
- throw new RangeError("Invalid input for Selection.fromJSON");
21974
- let cls = classesById[json.type];
21975
- if (!cls)
21976
- throw new RangeError(`No selection type ${json.type} defined`);
21977
- return cls.fromJSON(doc, json);
21894
+ invert() {
21895
+ return new RemoveMarkStep(this.from, this.to, this.mark);
21978
21896
  }
21979
- /**
21980
- To be able to deserialize selections from JSON, custom selection
21981
- classes must register themselves with an ID string, so that they
21982
- can be disambiguated. Try to pick something that's unlikely to
21983
- clash with classes from other modules.
21984
- */
21985
- static jsonID(id, selectionClass) {
21986
- if (id in classesById)
21987
- throw new RangeError("Duplicate use of selection JSON ID " + id);
21988
- classesById[id] = selectionClass;
21989
- selectionClass.prototype.jsonID = id;
21990
- return selectionClass;
21897
+ map(mapping) {
21898
+ let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21899
+ if (from.deleted && to.deleted || from.pos >= to.pos)
21900
+ return null;
21901
+ return new AddMarkStep(from.pos, to.pos, this.mark);
21902
+ }
21903
+ merge(other) {
21904
+ if (other instanceof AddMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from)
21905
+ return new AddMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
21906
+ return null;
21907
+ }
21908
+ toJSON() {
21909
+ return {
21910
+ stepType: "addMark",
21911
+ mark: this.mark.toJSON(),
21912
+ from: this.from,
21913
+ to: this.to
21914
+ };
21991
21915
  }
21992
21916
  /**
21993
- Get a [bookmark](https://prosemirror.net/docs/ref/#state.SelectionBookmark) for this selection,
21994
- which is a value that can be mapped without having access to a
21995
- current document, and later resolved to a real selection for a
21996
- given document again. (This is used mostly by the history to
21997
- track and restore old selections.) The default implementation of
21998
- this method just converts the selection to a text selection and
21999
- returns the bookmark for that.
21917
+ @internal
22000
21918
  */
22001
- getBookmark() {
22002
- return TextSelection.between(this.$anchor, this.$head).getBookmark();
21919
+ static fromJSON(schema, json) {
21920
+ if (typeof json.from != "number" || typeof json.to != "number")
21921
+ throw new RangeError("Invalid input for AddMarkStep.fromJSON");
21922
+ return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
22003
21923
  }
22004
21924
  }
22005
- Selection.prototype.visible = true;
22006
- class SelectionRange {
21925
+ Step.jsonID("addMark", AddMarkStep);
21926
+ class RemoveMarkStep extends Step {
22007
21927
  /**
22008
- Create a range.
21928
+ Create a mark-removing step.
22009
21929
  */
22010
- constructor($from, $to) {
22011
- this.$from = $from;
22012
- this.$to = $to;
21930
+ constructor(from, to, mark) {
21931
+ super();
21932
+ this.from = from;
21933
+ this.to = to;
21934
+ this.mark = mark;
22013
21935
  }
22014
- }
22015
- let warnedAboutTextSelection = false;
22016
- function checkTextSelection($pos) {
22017
- if (!warnedAboutTextSelection && !$pos.parent.inlineContent) {
22018
- warnedAboutTextSelection = true;
22019
- console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
21936
+ apply(doc) {
21937
+ let oldSlice = doc.slice(this.from, this.to);
21938
+ let slice = new Slice(mapFragment(oldSlice.content, (node) => {
21939
+ return node.mark(this.mark.removeFromSet(node.marks));
21940
+ }, doc), oldSlice.openStart, oldSlice.openEnd);
21941
+ return StepResult.fromReplace(doc, this.from, this.to, slice);
21942
+ }
21943
+ invert() {
21944
+ return new AddMarkStep(this.from, this.to, this.mark);
21945
+ }
21946
+ map(mapping) {
21947
+ let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
21948
+ if (from.deleted && to.deleted || from.pos >= to.pos)
21949
+ return null;
21950
+ return new RemoveMarkStep(from.pos, to.pos, this.mark);
21951
+ }
21952
+ merge(other) {
21953
+ if (other instanceof RemoveMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from)
21954
+ return new RemoveMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
21955
+ return null;
21956
+ }
21957
+ toJSON() {
21958
+ return {
21959
+ stepType: "removeMark",
21960
+ mark: this.mark.toJSON(),
21961
+ from: this.from,
21962
+ to: this.to
21963
+ };
22020
21964
  }
22021
- }
22022
- class TextSelection extends Selection {
22023
21965
  /**
22024
- Construct a text selection between the given points.
21966
+ @internal
22025
21967
  */
22026
- constructor($anchor, $head = $anchor) {
22027
- checkTextSelection($anchor);
22028
- checkTextSelection($head);
22029
- super($anchor, $head);
21968
+ static fromJSON(schema, json) {
21969
+ if (typeof json.from != "number" || typeof json.to != "number")
21970
+ throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");
21971
+ return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
22030
21972
  }
21973
+ }
21974
+ Step.jsonID("removeMark", RemoveMarkStep);
21975
+ class AddNodeMarkStep extends Step {
22031
21976
  /**
22032
- Returns a resolved position if this is a cursor selection (an
22033
- empty text selection), and null otherwise.
21977
+ Create a node mark step.
22034
21978
  */
22035
- get $cursor() {
22036
- return this.$anchor.pos == this.$head.pos ? this.$head : null;
21979
+ constructor(pos, mark) {
21980
+ super();
21981
+ this.pos = pos;
21982
+ this.mark = mark;
22037
21983
  }
22038
- map(doc, mapping) {
22039
- let $head = doc.resolve(mapping.map(this.head));
22040
- if (!$head.parent.inlineContent)
22041
- return Selection.near($head);
22042
- let $anchor = doc.resolve(mapping.map(this.anchor));
22043
- return new TextSelection($anchor.parent.inlineContent ? $anchor : $head, $head);
21984
+ apply(doc) {
21985
+ let node = doc.nodeAt(this.pos);
21986
+ if (!node)
21987
+ return StepResult.fail("No node at mark step's position");
21988
+ let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
21989
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
22044
21990
  }
22045
- replace(tr, content = Slice.empty) {
22046
- super.replace(tr, content);
22047
- if (content == Slice.empty) {
22048
- let marks = this.$from.marksAcross(this.$to);
22049
- if (marks)
22050
- tr.ensureMarks(marks);
21991
+ invert(doc) {
21992
+ let node = doc.nodeAt(this.pos);
21993
+ if (node) {
21994
+ let newSet = this.mark.addToSet(node.marks);
21995
+ if (newSet.length == node.marks.length) {
21996
+ for (let i = 0; i < node.marks.length; i++)
21997
+ if (!node.marks[i].isInSet(newSet))
21998
+ return new AddNodeMarkStep(this.pos, node.marks[i]);
21999
+ return new AddNodeMarkStep(this.pos, this.mark);
22000
+ }
22051
22001
  }
22002
+ return new RemoveNodeMarkStep(this.pos, this.mark);
22052
22003
  }
22053
- eq(other) {
22054
- return other instanceof TextSelection && other.anchor == this.anchor && other.head == this.head;
22055
- }
22056
- getBookmark() {
22057
- return new TextBookmark(this.anchor, this.head);
22004
+ map(mapping) {
22005
+ let pos = mapping.mapResult(this.pos, 1);
22006
+ return pos.deletedAfter ? null : new AddNodeMarkStep(pos.pos, this.mark);
22058
22007
  }
22059
22008
  toJSON() {
22060
- return { type: "text", anchor: this.anchor, head: this.head };
22009
+ return { stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON() };
22061
22010
  }
22062
22011
  /**
22063
22012
  @internal
22064
22013
  */
22065
- static fromJSON(doc, json) {
22066
- if (typeof json.anchor != "number" || typeof json.head != "number")
22067
- throw new RangeError("Invalid input for TextSelection.fromJSON");
22068
- return new TextSelection(doc.resolve(json.anchor), doc.resolve(json.head));
22014
+ static fromJSON(schema, json) {
22015
+ if (typeof json.pos != "number")
22016
+ throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON");
22017
+ return new AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
22069
22018
  }
22019
+ }
22020
+ Step.jsonID("addNodeMark", AddNodeMarkStep);
22021
+ class RemoveNodeMarkStep extends Step {
22070
22022
  /**
22071
- Create a text selection from non-resolved positions.
22023
+ Create a mark-removing step.
22072
22024
  */
22073
- static create(doc, anchor, head = anchor) {
22074
- let $anchor = doc.resolve(anchor);
22075
- return new this($anchor, head == anchor ? $anchor : doc.resolve(head));
22025
+ constructor(pos, mark) {
22026
+ super();
22027
+ this.pos = pos;
22028
+ this.mark = mark;
22029
+ }
22030
+ apply(doc) {
22031
+ let node = doc.nodeAt(this.pos);
22032
+ if (!node)
22033
+ return StepResult.fail("No node at mark step's position");
22034
+ let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
22035
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
22036
+ }
22037
+ invert(doc) {
22038
+ let node = doc.nodeAt(this.pos);
22039
+ if (!node || !this.mark.isInSet(node.marks))
22040
+ return this;
22041
+ return new AddNodeMarkStep(this.pos, this.mark);
22042
+ }
22043
+ map(mapping) {
22044
+ let pos = mapping.mapResult(this.pos, 1);
22045
+ return pos.deletedAfter ? null : new RemoveNodeMarkStep(pos.pos, this.mark);
22046
+ }
22047
+ toJSON() {
22048
+ return { stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON() };
22076
22049
  }
22077
22050
  /**
22078
- Return a text selection that spans the given positions or, if
22079
- they aren't text positions, find a text selection near them.
22080
- `bias` determines whether the method searches forward (default)
22081
- or backwards (negative number) first. Will fall back to calling
22082
- [`Selection.near`](https://prosemirror.net/docs/ref/#state.Selection^near) when the document
22083
- doesn't contain a valid text position.
22051
+ @internal
22084
22052
  */
22085
- static between($anchor, $head, bias) {
22086
- let dPos = $anchor.pos - $head.pos;
22087
- if (!bias || dPos)
22088
- bias = dPos >= 0 ? 1 : -1;
22089
- if (!$head.parent.inlineContent) {
22090
- let found2 = Selection.findFrom($head, bias, true) || Selection.findFrom($head, -bias, true);
22091
- if (found2)
22092
- $head = found2.$head;
22093
- else
22094
- return Selection.near($head, bias);
22095
- }
22096
- if (!$anchor.parent.inlineContent) {
22097
- if (dPos == 0) {
22098
- $anchor = $head;
22099
- } else {
22100
- $anchor = (Selection.findFrom($anchor, -bias, true) || Selection.findFrom($anchor, bias, true)).$anchor;
22101
- if ($anchor.pos < $head.pos != dPos < 0)
22102
- $anchor = $head;
22103
- }
22104
- }
22105
- return new TextSelection($anchor, $head);
22053
+ static fromJSON(schema, json) {
22054
+ if (typeof json.pos != "number")
22055
+ throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON");
22056
+ return new RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
22106
22057
  }
22107
22058
  }
22108
- Selection.jsonID("text", TextSelection);
22109
- class TextBookmark {
22110
- constructor(anchor, head) {
22111
- this.anchor = anchor;
22112
- this.head = head;
22059
+ Step.jsonID("removeNodeMark", RemoveNodeMarkStep);
22060
+ class ReplaceStep extends Step {
22061
+ /**
22062
+ The given `slice` should fit the 'gap' between `from` and
22063
+ `to`—the depths must line up, and the surrounding nodes must be
22064
+ able to be joined with the open sides of the slice. When
22065
+ `structure` is true, the step will fail if the content between
22066
+ from and to is not just a sequence of closing and then opening
22067
+ tokens (this is to guard against rebased replace steps
22068
+ overwriting something they weren't supposed to).
22069
+ */
22070
+ constructor(from, to, slice, structure = false) {
22071
+ super();
22072
+ this.from = from;
22073
+ this.to = to;
22074
+ this.slice = slice;
22075
+ this.structure = structure;
22076
+ }
22077
+ apply(doc) {
22078
+ if (this.structure && contentBetween(doc, this.from, this.to))
22079
+ return StepResult.fail("Structure replace would overwrite content");
22080
+ return StepResult.fromReplace(doc, this.from, this.to, this.slice);
22081
+ }
22082
+ getMap() {
22083
+ return new StepMap([this.from, this.to - this.from, this.slice.size]);
22084
+ }
22085
+ invert(doc) {
22086
+ return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to));
22113
22087
  }
22114
22088
  map(mapping) {
22115
- return new TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
22089
+ let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
22090
+ if (from.deletedAcross && to.deletedAcross)
22091
+ return null;
22092
+ return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
22116
22093
  }
22117
- resolve(doc) {
22118
- return TextSelection.between(doc.resolve(this.anchor), doc.resolve(this.head));
22094
+ merge(other) {
22095
+ if (!(other instanceof ReplaceStep) || other.structure || this.structure)
22096
+ return null;
22097
+ if (this.from + this.slice.size == other.from && !this.slice.openEnd && !other.slice.openStart) {
22098
+ let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(this.slice.content.append(other.slice.content), this.slice.openStart, other.slice.openEnd);
22099
+ return new ReplaceStep(this.from, this.to + (other.to - other.from), slice, this.structure);
22100
+ } else if (other.to == this.from && !this.slice.openStart && !other.slice.openEnd) {
22101
+ let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(other.slice.content.append(this.slice.content), other.slice.openStart, this.slice.openEnd);
22102
+ return new ReplaceStep(other.from, this.to, slice, this.structure);
22103
+ } else {
22104
+ return null;
22105
+ }
22106
+ }
22107
+ toJSON() {
22108
+ let json = { stepType: "replace", from: this.from, to: this.to };
22109
+ if (this.slice.size)
22110
+ json.slice = this.slice.toJSON();
22111
+ if (this.structure)
22112
+ json.structure = true;
22113
+ return json;
22114
+ }
22115
+ /**
22116
+ @internal
22117
+ */
22118
+ static fromJSON(schema, json) {
22119
+ if (typeof json.from != "number" || typeof json.to != "number")
22120
+ throw new RangeError("Invalid input for ReplaceStep.fromJSON");
22121
+ return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure);
22119
22122
  }
22120
22123
  }
22121
- class NodeSelection extends Selection {
22124
+ Step.jsonID("replace", ReplaceStep);
22125
+ class ReplaceAroundStep extends Step {
22122
22126
  /**
22123
- Create a node selection. Does not verify the validity of its
22124
- argument.
22127
+ Create a replace-around step with the given range and gap.
22128
+ `insert` should be the point in the slice into which the content
22129
+ of the gap should be moved. `structure` has the same meaning as
22130
+ it has in the [`ReplaceStep`](https://prosemirror.net/docs/ref/#transform.ReplaceStep) class.
22125
22131
  */
22126
- constructor($pos) {
22127
- let node = $pos.nodeAfter;
22128
- let $end = $pos.node(0).resolve($pos.pos + node.nodeSize);
22129
- super($pos, $end);
22130
- this.node = node;
22132
+ constructor(from, to, gapFrom, gapTo, slice, insert, structure = false) {
22133
+ super();
22134
+ this.from = from;
22135
+ this.to = to;
22136
+ this.gapFrom = gapFrom;
22137
+ this.gapTo = gapTo;
22138
+ this.slice = slice;
22139
+ this.insert = insert;
22140
+ this.structure = structure;
22131
22141
  }
22132
- map(doc, mapping) {
22133
- let { deleted, pos } = mapping.mapResult(this.anchor);
22134
- let $pos = doc.resolve(pos);
22135
- if (deleted)
22136
- return Selection.near($pos);
22137
- return new NodeSelection($pos);
22142
+ apply(doc) {
22143
+ if (this.structure && (contentBetween(doc, this.from, this.gapFrom) || contentBetween(doc, this.gapTo, this.to)))
22144
+ return StepResult.fail("Structure gap-replace would overwrite content");
22145
+ let gap = doc.slice(this.gapFrom, this.gapTo);
22146
+ if (gap.openStart || gap.openEnd)
22147
+ return StepResult.fail("Gap is not a flat range");
22148
+ let inserted = this.slice.insertAt(this.insert, gap.content);
22149
+ if (!inserted)
22150
+ return StepResult.fail("Content does not fit in gap");
22151
+ return StepResult.fromReplace(doc, this.from, this.to, inserted);
22138
22152
  }
22139
- content() {
22140
- return new Slice(Fragment.from(this.node), 0, 0);
22153
+ getMap() {
22154
+ return new StepMap([
22155
+ this.from,
22156
+ this.gapFrom - this.from,
22157
+ this.insert,
22158
+ this.gapTo,
22159
+ this.to - this.gapTo,
22160
+ this.slice.size - this.insert
22161
+ ]);
22162
+ }
22163
+ invert(doc) {
22164
+ let gap = this.gapTo - this.gapFrom;
22165
+ return new ReplaceAroundStep(this.from, this.from + this.slice.size + gap, this.from + this.insert, this.from + this.insert + gap, doc.slice(this.from, this.to).removeBetween(this.gapFrom - this.from, this.gapTo - this.from), this.gapFrom - this.from, this.structure);
22141
22166
  }
22142
- eq(other) {
22143
- return other instanceof NodeSelection && other.anchor == this.anchor;
22167
+ map(mapping) {
22168
+ let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
22169
+ let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1);
22170
+ let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1);
22171
+ if (from.deletedAcross && to.deletedAcross || gapFrom < from.pos || gapTo > to.pos)
22172
+ return null;
22173
+ return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
22144
22174
  }
22145
22175
  toJSON() {
22146
- return { type: "node", anchor: this.anchor };
22147
- }
22148
- getBookmark() {
22149
- return new NodeBookmark(this.anchor);
22176
+ let json = {
22177
+ stepType: "replaceAround",
22178
+ from: this.from,
22179
+ to: this.to,
22180
+ gapFrom: this.gapFrom,
22181
+ gapTo: this.gapTo,
22182
+ insert: this.insert
22183
+ };
22184
+ if (this.slice.size)
22185
+ json.slice = this.slice.toJSON();
22186
+ if (this.structure)
22187
+ json.structure = true;
22188
+ return json;
22150
22189
  }
22151
22190
  /**
22152
22191
  @internal
22153
22192
  */
22154
- static fromJSON(doc, json) {
22155
- if (typeof json.anchor != "number")
22156
- throw new RangeError("Invalid input for NodeSelection.fromJSON");
22157
- return new NodeSelection(doc.resolve(json.anchor));
22158
- }
22159
- /**
22160
- Create a node selection from non-resolved positions.
22161
- */
22162
- static create(doc, from) {
22163
- return new NodeSelection(doc.resolve(from));
22164
- }
22165
- /**
22166
- Determines whether the given node may be selected as a node
22167
- selection.
22168
- */
22169
- static isSelectable(node) {
22170
- return !node.isText && node.type.spec.selectable !== false;
22193
+ static fromJSON(schema, json) {
22194
+ if (typeof json.from != "number" || typeof json.to != "number" || typeof json.gapFrom != "number" || typeof json.gapTo != "number" || typeof json.insert != "number")
22195
+ throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");
22196
+ return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo, Slice.fromJSON(schema, json.slice), json.insert, !!json.structure);
22171
22197
  }
22172
22198
  }
22173
- NodeSelection.prototype.visible = false;
22174
- Selection.jsonID("node", NodeSelection);
22175
- class NodeBookmark {
22176
- constructor(anchor) {
22177
- this.anchor = anchor;
22178
- }
22179
- map(mapping) {
22180
- let { deleted, pos } = mapping.mapResult(this.anchor);
22181
- return deleted ? new TextBookmark(pos, pos) : new NodeBookmark(pos);
22199
+ Step.jsonID("replaceAround", ReplaceAroundStep);
22200
+ function contentBetween(doc, from, to) {
22201
+ let $from = doc.resolve(from), dist = to - from, depth = $from.depth;
22202
+ while (dist > 0 && depth > 0 && $from.indexAfter(depth) == $from.node(depth).childCount) {
22203
+ depth--;
22204
+ dist--;
22182
22205
  }
22183
- resolve(doc) {
22184
- let $pos = doc.resolve(this.anchor), node = $pos.nodeAfter;
22185
- if (node && NodeSelection.isSelectable(node))
22186
- return new NodeSelection($pos);
22187
- return Selection.near($pos);
22206
+ if (dist > 0) {
22207
+ let next = $from.node(depth).maybeChild($from.indexAfter(depth));
22208
+ while (dist > 0) {
22209
+ if (!next || next.isLeaf)
22210
+ return true;
22211
+ next = next.firstChild;
22212
+ dist--;
22213
+ }
22188
22214
  }
22215
+ return false;
22189
22216
  }
22190
- class AllSelection extends Selection {
22217
+ class AttrStep extends Step {
22191
22218
  /**
22192
- Create an all-selection over the given document.
22219
+ Construct an attribute step.
22193
22220
  */
22194
- constructor(doc) {
22195
- super(doc.resolve(0), doc.resolve(doc.content.size));
22221
+ constructor(pos, attr, value) {
22222
+ super();
22223
+ this.pos = pos;
22224
+ this.attr = attr;
22225
+ this.value = value;
22196
22226
  }
22197
- replace(tr, content = Slice.empty) {
22198
- if (content == Slice.empty) {
22199
- tr.delete(0, tr.doc.content.size);
22200
- let sel = Selection.atStart(tr.doc);
22201
- if (!sel.eq(tr.selection))
22202
- tr.setSelection(sel);
22203
- } else {
22204
- super.replace(tr, content);
22205
- }
22227
+ apply(doc) {
22228
+ let node = doc.nodeAt(this.pos);
22229
+ if (!node)
22230
+ return StepResult.fail("No node at attribute step's position");
22231
+ let attrs = /* @__PURE__ */ Object.create(null);
22232
+ for (let name in node.attrs)
22233
+ attrs[name] = node.attrs[name];
22234
+ attrs[this.attr] = this.value;
22235
+ let updated = node.type.create(attrs, null, node.marks);
22236
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
22206
22237
  }
22207
- toJSON() {
22208
- return { type: "all" };
22238
+ getMap() {
22239
+ return StepMap.empty;
22209
22240
  }
22210
- /**
22211
- @internal
22212
- */
22213
- static fromJSON(doc) {
22214
- return new AllSelection(doc);
22241
+ invert(doc) {
22242
+ return new AttrStep(this.pos, this.attr, doc.nodeAt(this.pos).attrs[this.attr]);
22215
22243
  }
22216
- map(doc) {
22217
- return new AllSelection(doc);
22244
+ map(mapping) {
22245
+ let pos = mapping.mapResult(this.pos, 1);
22246
+ return pos.deletedAfter ? null : new AttrStep(pos.pos, this.attr, this.value);
22218
22247
  }
22219
- eq(other) {
22220
- return other instanceof AllSelection;
22248
+ toJSON() {
22249
+ return { stepType: "attr", pos: this.pos, attr: this.attr, value: this.value };
22221
22250
  }
22222
- getBookmark() {
22223
- return AllBookmark;
22251
+ static fromJSON(schema, json) {
22252
+ if (typeof json.pos != "number" || typeof json.attr != "string")
22253
+ throw new RangeError("Invalid input for AttrStep.fromJSON");
22254
+ return new AttrStep(json.pos, json.attr, json.value);
22224
22255
  }
22225
22256
  }
22226
- Selection.jsonID("all", AllSelection);
22227
- const AllBookmark = {
22228
- map() {
22229
- return this;
22230
- },
22231
- resolve(doc) {
22232
- return new AllSelection(doc);
22257
+ Step.jsonID("attr", AttrStep);
22258
+ class DocAttrStep extends Step {
22259
+ /**
22260
+ Construct an attribute step.
22261
+ */
22262
+ constructor(attr, value) {
22263
+ super();
22264
+ this.attr = attr;
22265
+ this.value = value;
22233
22266
  }
22234
- };
22235
- function findSelectionIn(doc, node, pos, index2, dir, text2 = false) {
22236
- if (node.inlineContent)
22237
- return TextSelection.create(doc, pos);
22238
- for (let i = index2 - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
22239
- let child = node.child(i);
22240
- if (!child.isAtom) {
22241
- let inner = findSelectionIn(doc, child, pos + dir, dir < 0 ? child.childCount : 0, dir, text2);
22242
- if (inner)
22243
- return inner;
22244
- } else if (!text2 && NodeSelection.isSelectable(child)) {
22245
- return NodeSelection.create(doc, pos - (dir < 0 ? child.nodeSize : 0));
22246
- }
22247
- pos += child.nodeSize * dir;
22267
+ apply(doc) {
22268
+ let attrs = /* @__PURE__ */ Object.create(null);
22269
+ for (let name in doc.attrs)
22270
+ attrs[name] = doc.attrs[name];
22271
+ attrs[this.attr] = this.value;
22272
+ let updated = doc.type.create(attrs, doc.content, doc.marks);
22273
+ return StepResult.ok(updated);
22248
22274
  }
22249
- return null;
22250
- }
22251
- function selectionToInsertionEnd(tr, startLen, bias) {
22252
- let last2 = tr.steps.length - 1;
22253
- if (last2 < startLen)
22254
- return;
22255
- let step = tr.steps[last2];
22256
- if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep))
22257
- return;
22258
- let map = tr.mapping.maps[last2], end2;
22259
- map.forEach((_from, _to, _newFrom, newTo) => {
22260
- if (end2 == null)
22261
- end2 = newTo;
22262
- });
22263
- tr.setSelection(Selection.near(tr.doc.resolve(end2), bias));
22264
- }
22265
- function bind(f, self2) {
22266
- return !self2 || !f ? f : f.bind(self2);
22267
- }
22268
- class FieldDesc {
22269
- constructor(name, desc, self2) {
22270
- this.name = name;
22271
- this.init = bind(desc.init, self2);
22272
- this.apply = bind(desc.apply, self2);
22275
+ getMap() {
22276
+ return StepMap.empty;
22273
22277
  }
22274
- }
22275
- [
22276
- new FieldDesc("doc", {
22277
- init(config) {
22278
- return config.doc || config.schema.topNodeType.createAndFill();
22279
- },
22280
- apply(tr) {
22281
- return tr.doc;
22282
- }
22283
- }),
22284
- new FieldDesc("selection", {
22285
- init(config, instance) {
22286
- return config.selection || Selection.atStart(instance.doc);
22287
- },
22288
- apply(tr) {
22289
- return tr.selection;
22290
- }
22291
- }),
22292
- new FieldDesc("storedMarks", {
22293
- init(config) {
22294
- return config.storedMarks || null;
22295
- },
22296
- apply(tr, _marks, _old, state2) {
22297
- return state2.selection.$cursor ? tr.storedMarks : null;
22298
- }
22299
- }),
22300
- new FieldDesc("scrollToSelection", {
22301
- init() {
22302
- return 0;
22303
- },
22304
- apply(tr, prev) {
22305
- return tr.scrolledIntoView ? prev + 1 : prev;
22306
- }
22307
- })
22308
- ];
22309
- function bindProps(obj, self2, target) {
22310
- for (let prop in obj) {
22311
- let val = obj[prop];
22312
- if (val instanceof Function)
22313
- val = val.bind(self2);
22314
- else if (prop == "handleDOMEvents")
22315
- val = bindProps(val, self2, {});
22316
- target[prop] = val;
22278
+ invert(doc) {
22279
+ return new DocAttrStep(this.attr, doc.attrs[this.attr]);
22317
22280
  }
22318
- return target;
22319
- }
22320
- class Plugin {
22321
- /**
22322
- Create a plugin.
22323
- */
22324
- constructor(spec) {
22325
- this.spec = spec;
22326
- this.props = {};
22327
- if (spec.props)
22328
- bindProps(spec.props, this, this.props);
22329
- this.key = spec.key ? spec.key.key : createKey("plugin");
22281
+ map(mapping) {
22282
+ return this;
22330
22283
  }
22331
- /**
22332
- Extract the plugin's state field from an editor state.
22333
- */
22334
- getState(state2) {
22335
- return state2[this.key];
22284
+ toJSON() {
22285
+ return { stepType: "docAttr", attr: this.attr, value: this.value };
22286
+ }
22287
+ static fromJSON(schema, json) {
22288
+ if (typeof json.attr != "string")
22289
+ throw new RangeError("Invalid input for DocAttrStep.fromJSON");
22290
+ return new DocAttrStep(json.attr, json.value);
22336
22291
  }
22337
22292
  }
22338
- const keys = /* @__PURE__ */ Object.create(null);
22339
- function createKey(name) {
22340
- if (name in keys)
22341
- return name + "$" + ++keys[name];
22342
- keys[name] = 0;
22343
- return name + "$";
22344
- }
22345
- class PluginKey {
22293
+ Step.jsonID("docAttr", DocAttrStep);
22294
+ let TransformError = class extends Error {
22295
+ };
22296
+ TransformError = function TransformError2(message) {
22297
+ let err = Error.call(this, message);
22298
+ err.__proto__ = TransformError2.prototype;
22299
+ return err;
22300
+ };
22301
+ TransformError.prototype = Object.create(Error.prototype);
22302
+ TransformError.prototype.constructor = TransformError;
22303
+ TransformError.prototype.name = "TransformError";
22304
+ const classesById = /* @__PURE__ */ Object.create(null);
22305
+ class Selection {
22346
22306
  /**
22347
- Create a plugin key.
22307
+ Initialize a selection with the head and anchor and ranges. If no
22308
+ ranges are given, constructs a single range across `$anchor` and
22309
+ `$head`.
22348
22310
  */
22349
- constructor(name = "key") {
22350
- this.key = createKey(name);
22311
+ constructor($anchor, $head, ranges) {
22312
+ this.$anchor = $anchor;
22313
+ this.$head = $head;
22314
+ this.ranges = ranges || [new SelectionRange($anchor.min($head), $anchor.max($head))];
22351
22315
  }
22352
22316
  /**
22353
- Get the active plugin with this key, if any, from an editor
22354
- state.
22317
+ The selection's anchor, as an unresolved position.
22355
22318
  */
22356
- get(state2) {
22357
- return state2.config.pluginsByKey[this.key];
22319
+ get anchor() {
22320
+ return this.$anchor.pos;
22358
22321
  }
22359
22322
  /**
22360
- Get the plugin's state from an editor state.
22323
+ The selection's head.
22361
22324
  */
22362
- getState(state2) {
22363
- return state2[this.key];
22364
- }
22365
- }
22366
- var NAME = "AspectRatio";
22367
- var AspectRatio$1 = React__namespace.forwardRef(
22368
- (props, forwardedRef) => {
22369
- const { ratio = 1 / 1, style, ...aspectRatioProps } = props;
22370
- return /* @__PURE__ */ jsxRuntime.jsx(
22371
- "div",
22372
- {
22373
- style: {
22374
- // ensures inner element is contained
22375
- position: "relative",
22376
- // ensures padding bottom trick maths works
22377
- width: "100%",
22378
- paddingBottom: `${100 / ratio}%`
22379
- },
22380
- "data-radix-aspect-ratio-wrapper": "",
22381
- children: /* @__PURE__ */ jsxRuntime.jsx(
22382
- Primitive.div,
22383
- {
22384
- ...aspectRatioProps,
22385
- ref: forwardedRef,
22386
- style: {
22387
- ...style,
22388
- // ensures children expand in ratio
22389
- position: "absolute",
22390
- top: 0,
22391
- right: 0,
22392
- bottom: 0,
22393
- left: 0
22394
- }
22395
- }
22396
- )
22397
- }
22398
- );
22399
- }
22400
- );
22401
- AspectRatio$1.displayName = NAME;
22402
- var Root = AspectRatio$1;
22403
- function AspectRatio({ ...props }) {
22404
- return /* @__PURE__ */ jsxRuntime.jsx(Root, { "data-slot": "aspect-ratio", ...props });
22405
- }
22406
- const MediaNodeView = ({ node, updateAttributes, editor, getPos }) => {
22407
- const [selected, setSelected] = React.useState(false);
22408
- React.useEffect(() => {
22409
- const checkSelection = () => {
22410
- const selection = editor.state.selection;
22411
- const selectedNode = selection instanceof NodeSelection ? selection.node : null;
22412
- if (selectedNode === node) requestAnimationFrame(() => setSelected(true));
22413
- else setSelected(false);
22414
- };
22415
- checkSelection();
22416
- editor.on("selectionUpdate", checkSelection);
22417
- return () => {
22418
- editor.off("selectionUpdate", checkSelection);
22419
- };
22420
- }, [editor, node]);
22421
- const handleDelete = () => {
22422
- const pos = getPos() ?? NaN;
22423
- if (!Number.isFinite(pos)) return;
22424
- editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).focus().run();
22425
- };
22426
- const handleAlign = (align) => updateAttributes({ align });
22427
- if (node.attrs.uploading) {
22428
- return /* @__PURE__ */ jsxRuntime.jsx(
22429
- react.NodeViewWrapper,
22430
- {
22431
- className: cn(
22432
- "flex my-4",
22433
- node.attrs.align === Alignment.CENTER ? "justify-center" : node.attrs.align === Alignment.RIGHT ? "justify-end" : ""
22434
- ),
22435
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-full", children: [
22436
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative bg-muted rounded-lg overflow-hidden", children: [
22437
- /* @__PURE__ */ jsxRuntime.jsx(
22438
- AspectRatio,
22439
- {
22440
- ratio: node.attrs.type === MediaNodeTypes.Audio ? 4 / 1 : 16 / 4,
22441
- className: cn("flex items-center justify-center bg-muted"),
22442
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center space-y-3", children: [
22443
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center", children: [
22444
- node.attrs.type === MediaNodeTypes.Image && /* @__PURE__ */ jsxRuntime.jsx(Image$1, { className: "w-12 h-12 text-muted-foreground/50" }),
22445
- node.attrs.type === MediaNodeTypes.Video && /* @__PURE__ */ jsxRuntime.jsx(Video, { className: "w-12 h-12 text-muted-foreground/50" }),
22446
- node.attrs.type === MediaNodeTypes.Audio && /* @__PURE__ */ jsxRuntime.jsx(Music, { className: "w-12 h-12 text-muted-foreground/50" })
22447
- ] }),
22448
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground font-medium", children: [
22449
- "Uploading ",
22450
- node.attrs.type,
22451
- "..."
22452
- ] })
22453
- ] })
22454
- }
22455
- ),
22456
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(Progress, { value: node.attrs.progress ?? 0, className: "h-2" }) }),
22457
- /* @__PURE__ */ jsxRuntime.jsx(
22458
- "button",
22459
- {
22460
- onClick: handleDelete,
22461
- className: "absolute top-2 right-2 p-2 bg-background/90 hover:bg-background rounded-full shadow-sm transition-colors",
22462
- title: "Cancel upload",
22463
- children: /* @__PURE__ */ jsxRuntime.jsx(X, { className: "w-4 h-4" })
22464
- }
22465
- )
22466
- ] }),
22467
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground text-center mt-2", children: [
22468
- Math.round(node.attrs.progress ?? 0),
22469
- "%"
22470
- ] })
22471
- ] })
22472
- }
22473
- );
22325
+ get head() {
22326
+ return this.$head.pos;
22474
22327
  }
22475
- return /* @__PURE__ */ jsxRuntime.jsx(
22476
- react.NodeViewWrapper,
22477
- {
22478
- "data-drag-handle": true,
22479
- className: cn(
22480
- "flex",
22481
- node.attrs.align === Alignment.CENTER ? "justify-center" : node.attrs.align === Alignment.RIGHT ? "justify-end" : ""
22482
- ),
22483
- children: node.attrs.type === MediaNodeTypes.Image ? /* @__PURE__ */ jsxRuntime.jsx(
22484
- ResizableWithToolbarWrapper,
22485
- {
22486
- open: selected,
22487
- mediaType: node.attrs.type,
22488
- align: node.attrs.align,
22489
- onResize: (maxWidth) => updateAttributes({ maxWidth }),
22490
- onAlign: handleAlign,
22491
- onDelete: handleDelete,
22492
- children: /* @__PURE__ */ jsxRuntime.jsx(
22493
- "img",
22494
- {
22495
- src: node.attrs.src,
22496
- alt: node.attrs.caption,
22497
- className: cn(
22498
- "object-contain w-full h-auto rounded-lg",
22499
- selected && "border-2 border-blue-500"
22500
- )
22501
- }
22502
- )
22503
- }
22504
- ) : node.attrs.type === MediaNodeTypes.Video ? /* @__PURE__ */ jsxRuntime.jsx(
22505
- ResizableWithToolbarWrapper,
22506
- {
22507
- open: selected,
22508
- mediaType: node.attrs.type,
22509
- align: node.attrs.align,
22510
- onResize: (maxWidth) => updateAttributes({ maxWidth }),
22511
- onAlign: handleAlign,
22512
- onDelete: handleDelete,
22513
- children: /* @__PURE__ */ jsxRuntime.jsx(
22514
- "video",
22515
- {
22516
- src: node.attrs.src,
22517
- controls: true,
22518
- className: cn("object-contain w-full h-auto", selected && "border-2 border-blue-500")
22519
- }
22520
- )
22521
- }
22522
- ) : node.attrs.type === MediaNodeTypes.Audio ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative p-4 bg-accent/20 border border-border/20 w-full", children: [
22523
- /* @__PURE__ */ jsxRuntime.jsx(
22524
- EditMediaToolbar,
22525
- {
22526
- open: selected,
22527
- align: node.attrs.align,
22528
- onAlign: handleAlign,
22529
- onDelete: handleDelete
22530
- }
22531
- ),
22532
- /* @__PURE__ */ jsxRuntime.jsx(
22533
- "audio",
22534
- {
22535
- src: node.attrs.src,
22536
- controls: true,
22537
- className: cn("w-full", selected && "rounded-full border-2 border-blue-500")
22538
- }
22539
- )
22540
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-sm text-gray-600", children: "Unsupported media" })
22541
- }
22542
- );
22543
- };
22544
- const ResizableWithToolbarWrapper = ({
22545
- open,
22546
- align,
22547
- onAlign,
22548
- onDelete,
22549
- onResize,
22550
- mediaType,
22551
- children
22552
- }) => {
22553
- return /* @__PURE__ */ jsxRuntime.jsxs(
22554
- ResizableWrapper,
22555
- {
22556
- onResize,
22557
- className: cn(mediaType === MediaNodeTypes.Audio && "relative p-4 bg-accent/50"),
22558
- children: [
22559
- /* @__PURE__ */ jsxRuntime.jsx(EditMediaToolbar, { open, align, onAlign, onDelete }),
22560
- children
22561
- ]
22562
- }
22563
- );
22564
- };
22565
- const createDeleteByUidCommand = (nodeName) => {
22566
- return (uid) => ({ state: state2, commands }) => {
22567
- const { doc } = state2;
22568
- let targetPos = null;
22569
- let targetSize = null;
22570
- doc.descendants((node, pos) => {
22571
- if (node.type.name === nodeName && node.attrs.uid === uid) {
22572
- targetPos = pos;
22573
- targetSize = node.nodeSize;
22328
+ /**
22329
+ The lower bound of the selection's main range.
22330
+ */
22331
+ get from() {
22332
+ return this.$from.pos;
22333
+ }
22334
+ /**
22335
+ The upper bound of the selection's main range.
22336
+ */
22337
+ get to() {
22338
+ return this.$to.pos;
22339
+ }
22340
+ /**
22341
+ The resolved lower bound of the selection's main range.
22342
+ */
22343
+ get $from() {
22344
+ return this.ranges[0].$from;
22345
+ }
22346
+ /**
22347
+ The resolved upper bound of the selection's main range.
22348
+ */
22349
+ get $to() {
22350
+ return this.ranges[0].$to;
22351
+ }
22352
+ /**
22353
+ Indicates whether the selection contains any content.
22354
+ */
22355
+ get empty() {
22356
+ let ranges = this.ranges;
22357
+ for (let i = 0; i < ranges.length; i++)
22358
+ if (ranges[i].$from.pos != ranges[i].$to.pos)
22574
22359
  return false;
22575
- }
22576
- });
22577
- if (targetPos !== null && targetSize !== null) {
22578
- return commands.deleteRange({
22579
- from: targetPos,
22580
- to: Number(targetPos) + Number(targetSize)
22581
- });
22360
+ return true;
22361
+ }
22362
+ /**
22363
+ Get the content of this selection as a slice.
22364
+ */
22365
+ content() {
22366
+ return this.$from.doc.slice(this.from, this.to, true);
22367
+ }
22368
+ /**
22369
+ Replace the selection with a slice or, if no slice is given,
22370
+ delete the selection. Will append to the given transaction.
22371
+ */
22372
+ replace(tr, content = Slice.empty) {
22373
+ let lastNode = content.content.lastChild, lastParent = null;
22374
+ for (let i = 0; i < content.openEnd; i++) {
22375
+ lastParent = lastNode;
22376
+ lastNode = lastNode.lastChild;
22582
22377
  }
22583
- return false;
22584
- };
22585
- };
22586
- const createUpdateByUidCommand = (nodeName) => {
22587
- return (uid, attrs) => ({ state: state2, dispatch }) => {
22588
- if (!dispatch) return false;
22589
- const { doc, tr } = state2;
22590
- let found2 = false;
22591
- doc.descendants((node, pos) => {
22592
- if (node.type.name === nodeName && node.attrs.uid === uid) {
22593
- tr.setNodeMarkup(pos, void 0, {
22594
- ...node.attrs,
22595
- ...attrs
22596
- });
22597
- found2 = true;
22598
- return false;
22599
- }
22600
- });
22601
- if (found2) {
22602
- dispatch(tr);
22603
- return true;
22378
+ let mapFrom = tr.steps.length, ranges = this.ranges;
22379
+ for (let i = 0; i < ranges.length; i++) {
22380
+ let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
22381
+ tr.replaceRange(mapping.map($from.pos), mapping.map($to.pos), i ? Slice.empty : content);
22382
+ if (i == 0)
22383
+ selectionToInsertionEnd(tr, mapFrom, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1);
22604
22384
  }
22605
- return false;
22606
- };
22607
- };
22608
- const createDeleteMarkByUidCommand = (markName) => {
22609
- return (uid) => ({
22610
- state: state2,
22611
- dispatch
22612
- }) => {
22613
- if (!dispatch) return false;
22614
- const { doc, schema } = state2;
22615
- let tr = state2.tr;
22616
- let found2 = false;
22617
- const positions = [];
22618
- doc.descendants((node, pos) => {
22619
- if (node.isText && node.marks?.length) {
22620
- node.marks.forEach((mark) => {
22621
- if (mark.type.name === markName && mark.attrs.uid === uid) {
22622
- positions.push({
22623
- from: pos,
22624
- to: pos + node.nodeSize
22625
- });
22626
- found2 = true;
22627
- }
22628
- });
22629
- }
22630
- });
22631
- if (found2) {
22632
- const markType = schema.marks[markName];
22633
- if (markType) {
22634
- positions.forEach(({ from, to }) => {
22635
- tr = tr.removeMark(from, to, markType);
22636
- });
22637
- dispatch(tr);
22385
+ }
22386
+ /**
22387
+ Replace the selection with the given node, appending the changes
22388
+ to the given transaction.
22389
+ */
22390
+ replaceWith(tr, node) {
22391
+ let mapFrom = tr.steps.length, ranges = this.ranges;
22392
+ for (let i = 0; i < ranges.length; i++) {
22393
+ let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
22394
+ let from = mapping.map($from.pos), to = mapping.map($to.pos);
22395
+ if (i) {
22396
+ tr.deleteRange(from, to);
22397
+ } else {
22398
+ tr.replaceRangeWith(from, to, node);
22399
+ selectionToInsertionEnd(tr, mapFrom, node.isInline ? -1 : 1);
22638
22400
  }
22639
22401
  }
22640
- return found2;
22641
- };
22642
- };
22643
- const createUpdateMarkByUidCommand = (markName) => {
22644
- return (uid, attrs) => ({
22645
- state: state2,
22646
- dispatch
22647
- }) => {
22648
- console.log("=== UPDATE MARK COMMAND START ===");
22649
- console.log("markName:", markName);
22650
- console.log("uid:", uid);
22651
- console.log("attrs:", attrs);
22652
- console.log("state.doc:", state2.doc.toJSON());
22653
- if (!dispatch) {
22654
- console.log("No dispatch, returning false");
22655
- return false;
22402
+ }
22403
+ /**
22404
+ Find a valid cursor or leaf node selection starting at the given
22405
+ position and searching back if `dir` is negative, and forward if
22406
+ positive. When `textOnly` is true, only consider cursor
22407
+ selections. Will return null when no valid selection position is
22408
+ found.
22409
+ */
22410
+ static findFrom($pos, dir, textOnly = false) {
22411
+ let inner = $pos.parent.inlineContent ? new TextSelection($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
22412
+ if (inner)
22413
+ return inner;
22414
+ for (let depth = $pos.depth - 1; depth >= 0; depth--) {
22415
+ let found2 = dir < 0 ? findSelectionIn($pos.node(0), $pos.node(depth), $pos.before(depth + 1), $pos.index(depth), dir, textOnly) : findSelectionIn($pos.node(0), $pos.node(depth), $pos.after(depth + 1), $pos.index(depth) + 1, dir, textOnly);
22416
+ if (found2)
22417
+ return found2;
22418
+ }
22419
+ return null;
22420
+ }
22421
+ /**
22422
+ Find a valid cursor or leaf node selection near the given
22423
+ position. Searches forward first by default, but if `bias` is
22424
+ negative, it will search backwards first.
22425
+ */
22426
+ static near($pos, bias = 1) {
22427
+ return this.findFrom($pos, bias) || this.findFrom($pos, -bias) || new AllSelection($pos.node(0));
22428
+ }
22429
+ /**
22430
+ Find the cursor or leaf node selection closest to the start of
22431
+ the given document. Will return an
22432
+ [`AllSelection`](https://prosemirror.net/docs/ref/#state.AllSelection) if no valid position
22433
+ exists.
22434
+ */
22435
+ static atStart(doc) {
22436
+ return findSelectionIn(doc, doc, 0, 0, 1) || new AllSelection(doc);
22437
+ }
22438
+ /**
22439
+ Find the cursor or leaf node selection closest to the end of the
22440
+ given document.
22441
+ */
22442
+ static atEnd(doc) {
22443
+ return findSelectionIn(doc, doc, doc.content.size, doc.childCount, -1) || new AllSelection(doc);
22444
+ }
22445
+ /**
22446
+ Deserialize the JSON representation of a selection. Must be
22447
+ implemented for custom classes (as a static class method).
22448
+ */
22449
+ static fromJSON(doc, json) {
22450
+ if (!json || !json.type)
22451
+ throw new RangeError("Invalid input for Selection.fromJSON");
22452
+ let cls = classesById[json.type];
22453
+ if (!cls)
22454
+ throw new RangeError(`No selection type ${json.type} defined`);
22455
+ return cls.fromJSON(doc, json);
22456
+ }
22457
+ /**
22458
+ To be able to deserialize selections from JSON, custom selection
22459
+ classes must register themselves with an ID string, so that they
22460
+ can be disambiguated. Try to pick something that's unlikely to
22461
+ clash with classes from other modules.
22462
+ */
22463
+ static jsonID(id, selectionClass) {
22464
+ if (id in classesById)
22465
+ throw new RangeError("Duplicate use of selection JSON ID " + id);
22466
+ classesById[id] = selectionClass;
22467
+ selectionClass.prototype.jsonID = id;
22468
+ return selectionClass;
22469
+ }
22470
+ /**
22471
+ Get a [bookmark](https://prosemirror.net/docs/ref/#state.SelectionBookmark) for this selection,
22472
+ which is a value that can be mapped without having access to a
22473
+ current document, and later resolved to a real selection for a
22474
+ given document again. (This is used mostly by the history to
22475
+ track and restore old selections.) The default implementation of
22476
+ this method just converts the selection to a text selection and
22477
+ returns the bookmark for that.
22478
+ */
22479
+ getBookmark() {
22480
+ return TextSelection.between(this.$anchor, this.$head).getBookmark();
22481
+ }
22482
+ }
22483
+ Selection.prototype.visible = true;
22484
+ class SelectionRange {
22485
+ /**
22486
+ Create a range.
22487
+ */
22488
+ constructor($from, $to) {
22489
+ this.$from = $from;
22490
+ this.$to = $to;
22491
+ }
22492
+ }
22493
+ let warnedAboutTextSelection = false;
22494
+ function checkTextSelection($pos) {
22495
+ if (!warnedAboutTextSelection && !$pos.parent.inlineContent) {
22496
+ warnedAboutTextSelection = true;
22497
+ console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
22498
+ }
22499
+ }
22500
+ class TextSelection extends Selection {
22501
+ /**
22502
+ Construct a text selection between the given points.
22503
+ */
22504
+ constructor($anchor, $head = $anchor) {
22505
+ checkTextSelection($anchor);
22506
+ checkTextSelection($head);
22507
+ super($anchor, $head);
22508
+ }
22509
+ /**
22510
+ Returns a resolved position if this is a cursor selection (an
22511
+ empty text selection), and null otherwise.
22512
+ */
22513
+ get $cursor() {
22514
+ return this.$anchor.pos == this.$head.pos ? this.$head : null;
22515
+ }
22516
+ map(doc, mapping) {
22517
+ let $head = doc.resolve(mapping.map(this.head));
22518
+ if (!$head.parent.inlineContent)
22519
+ return Selection.near($head);
22520
+ let $anchor = doc.resolve(mapping.map(this.anchor));
22521
+ return new TextSelection($anchor.parent.inlineContent ? $anchor : $head, $head);
22522
+ }
22523
+ replace(tr, content = Slice.empty) {
22524
+ super.replace(tr, content);
22525
+ if (content == Slice.empty) {
22526
+ let marks = this.$from.marksAcross(this.$to);
22527
+ if (marks)
22528
+ tr.ensureMarks(marks);
22656
22529
  }
22657
- const { doc, schema } = state2;
22658
- let tr = state2.tr;
22659
- let found2 = false;
22660
- const positions = [];
22661
- doc.descendants((node, pos) => {
22662
- if (node.isText && node.marks?.length) {
22663
- node.marks.forEach((mark) => {
22664
- console.log("Checking mark:", {
22665
- markType: mark.type.name,
22666
- markUid: mark.attrs.uid,
22667
- targetMarkName: markName,
22668
- targetUid: uid,
22669
- matches: mark.type.name === markName && mark.attrs.uid === uid
22670
- });
22671
- if (mark.type.name === markName && mark.attrs.uid === uid) {
22672
- positions.push({
22673
- from: pos,
22674
- to: pos + node.nodeSize,
22675
- mark
22676
- });
22677
- found2 = true;
22678
- console.log("Found matching mark at:", { from: pos, to: pos + node.nodeSize });
22679
- }
22680
- });
22681
- }
22682
- });
22683
- console.log("Total positions found:", positions.length);
22684
- console.log("Positions:", positions);
22685
- if (found2) {
22686
- positions.forEach(({ from, to, mark }) => {
22687
- const newAttrs = { ...mark.attrs, ...attrs };
22688
- const markType = schema.marks[markName];
22689
- console.log("Applying update:", { from, to, oldAttrs: mark.attrs, newAttrs });
22690
- if (markType) {
22691
- console.log("Before removeMark - tr.doc:", tr.doc.toJSON());
22692
- tr = tr.removeMark(from, to, markType);
22693
- console.log("After removeMark - tr.doc:", tr.doc.toJSON());
22694
- tr = tr.addMark(from, to, markType.create(newAttrs));
22695
- console.log("After addMark - tr.doc:", tr.doc.toJSON());
22696
- }
22697
- });
22698
- console.log("About to dispatch transaction");
22699
- console.log("Final tr.doc:", tr.doc.toJSON());
22700
- console.log("Original state.doc:", state2.doc.toJSON());
22701
- dispatch(tr);
22702
- console.log("Transaction dispatched successfully");
22530
+ }
22531
+ eq(other) {
22532
+ return other instanceof TextSelection && other.anchor == this.anchor && other.head == this.head;
22533
+ }
22534
+ getBookmark() {
22535
+ return new TextBookmark(this.anchor, this.head);
22536
+ }
22537
+ toJSON() {
22538
+ return { type: "text", anchor: this.anchor, head: this.head };
22539
+ }
22540
+ /**
22541
+ @internal
22542
+ */
22543
+ static fromJSON(doc, json) {
22544
+ if (typeof json.anchor != "number" || typeof json.head != "number")
22545
+ throw new RangeError("Invalid input for TextSelection.fromJSON");
22546
+ return new TextSelection(doc.resolve(json.anchor), doc.resolve(json.head));
22547
+ }
22548
+ /**
22549
+ Create a text selection from non-resolved positions.
22550
+ */
22551
+ static create(doc, anchor, head = anchor) {
22552
+ let $anchor = doc.resolve(anchor);
22553
+ return new this($anchor, head == anchor ? $anchor : doc.resolve(head));
22554
+ }
22555
+ /**
22556
+ Return a text selection that spans the given positions or, if
22557
+ they aren't text positions, find a text selection near them.
22558
+ `bias` determines whether the method searches forward (default)
22559
+ or backwards (negative number) first. Will fall back to calling
22560
+ [`Selection.near`](https://prosemirror.net/docs/ref/#state.Selection^near) when the document
22561
+ doesn't contain a valid text position.
22562
+ */
22563
+ static between($anchor, $head, bias) {
22564
+ let dPos = $anchor.pos - $head.pos;
22565
+ if (!bias || dPos)
22566
+ bias = dPos >= 0 ? 1 : -1;
22567
+ if (!$head.parent.inlineContent) {
22568
+ let found2 = Selection.findFrom($head, bias, true) || Selection.findFrom($head, -bias, true);
22569
+ if (found2)
22570
+ $head = found2.$head;
22571
+ else
22572
+ return Selection.near($head, bias);
22703
22573
  }
22704
- console.log("=== UPDATE MARK COMMAND END ===");
22705
- return found2;
22706
- };
22707
- };
22708
- const DEFAULT_MEDIA_ATTRS = {
22709
- uid: "",
22710
- src: "",
22711
- type: MediaNodeTypes.Image,
22712
- style: "object-fit: contain; width: 100%; border-radius: 0.5rem;",
22713
- maxWidth: MEDIA_MIN_WIDTH,
22714
- align: Alignment.LEFT,
22715
- caption: "",
22716
- uploading: false,
22717
- progress: 0
22718
- };
22719
- const MediaNode = core.Node.create({
22720
- name: "media",
22721
- group: "block",
22722
- atom: true,
22723
- addAttributes() {
22724
- return {
22725
- uid: { default: DEFAULT_MEDIA_ATTRS.uid },
22726
- src: { default: DEFAULT_MEDIA_ATTRS.src },
22727
- type: { default: DEFAULT_MEDIA_ATTRS.type },
22728
- style: { default: DEFAULT_MEDIA_ATTRS.style },
22729
- maxWidth: { default: DEFAULT_MEDIA_ATTRS.maxWidth },
22730
- align: { default: DEFAULT_MEDIA_ATTRS.align },
22731
- caption: { default: DEFAULT_MEDIA_ATTRS.caption },
22732
- uploading: { default: DEFAULT_MEDIA_ATTRS.uploading },
22733
- progress: { default: DEFAULT_MEDIA_ATTRS.progress },
22734
- payload: { default: null, parseHTML: () => null, renderHTML: () => ({}) }
22735
- };
22736
- },
22737
- draggable() {
22738
- return Boolean(!this?.options?.attrs?.uploading && !this?.options?.attrs?.mediaMode);
22739
- },
22740
- selectable() {
22741
- return Boolean(!this?.options?.attrs?.mediaMode);
22742
- },
22743
- parseHTML() {
22744
- return [
22745
- {
22746
- tag: "div[data-media]",
22747
- getAttrs: (dom) => {
22748
- return {
22749
- uid: dom.getAttribute("data-uid") || "",
22750
- src: dom.getAttribute("data-src") || "",
22751
- type: dom.getAttribute("data-type") || MediaNodeTypes.Image,
22752
- align: dom.getAttribute("data-align") || Alignment.LEFT,
22753
- caption: dom.getAttribute("data-caption") || "",
22754
- style: dom.getAttribute("style") || DEFAULT_MEDIA_ATTRS.style
22755
- };
22756
- }
22757
- }
22758
- ];
22759
- },
22760
- renderHTML({ HTMLAttributes }) {
22761
- const { uid, src, style, type, align, maxWidth, caption } = HTMLAttributes;
22762
- const tag = MEDIA_TAGS[type];
22763
- const wrapperAttrs = core.mergeAttributes({
22764
- style: buildWrapperStyle(align),
22765
- "data-media": true,
22766
- "data-uid": uid,
22767
- "data-type": type,
22768
- "data-src": src,
22769
- "data-caption": caption || "",
22770
- "data-align": align
22771
- });
22772
- const mediaAttrs = core.mergeAttributes(
22773
- { src, style: buildMediaStyle(style, type, maxWidth) },
22774
- { controls: type === MediaNodeTypes.Audio || type === MediaNodeTypes.Video }
22775
- );
22776
- return ["div", wrapperAttrs, [tag, mediaAttrs]];
22777
- },
22778
- addNodeView() {
22779
- return react.ReactNodeViewRenderer(MediaNodeView);
22780
- },
22781
- addCommands() {
22782
- return {
22783
- insertMedia: (options) => ({ commands, chain }) => {
22784
- if (this?.options?.attrs?.mediaMode) {
22785
- return chain().insertContent([{ type: this.name, attrs: options }]).command(({ tr, dispatch }) => {
22786
- if (dispatch) {
22787
- const endPos = tr.doc.content.size;
22788
- const $pos = tr.doc.resolve(endPos);
22789
- tr.setSelection(state.TextSelection.near($pos));
22790
- }
22791
- return true;
22792
- }).run();
22793
- }
22794
- return commands.insertContent([
22795
- { type: this.name, attrs: options },
22796
- { type: "paragraph" }
22797
- //! Fixes multiple media overlapping issue (But removes default selection feature)
22798
- ]);
22799
- },
22800
- updateMediaByUid: createUpdateByUidCommand(this.name),
22801
- deleteMediaByUid: createDeleteByUidCommand(this.name),
22802
- updateMediaProgress: (uid, progress) => ({ commands }) => {
22803
- return commands.updateMediaByUid(uid, { progress });
22574
+ if (!$anchor.parent.inlineContent) {
22575
+ if (dPos == 0) {
22576
+ $anchor = $head;
22577
+ } else {
22578
+ $anchor = (Selection.findFrom($anchor, -bias, true) || Selection.findFrom($anchor, bias, true)).$anchor;
22579
+ if ($anchor.pos < $head.pos != dPos < 0)
22580
+ $anchor = $head;
22804
22581
  }
22805
- };
22582
+ }
22583
+ return new TextSelection($anchor, $head);
22584
+ }
22585
+ }
22586
+ Selection.jsonID("text", TextSelection);
22587
+ class TextBookmark {
22588
+ constructor(anchor, head) {
22589
+ this.anchor = anchor;
22590
+ this.head = head;
22591
+ }
22592
+ map(mapping) {
22593
+ return new TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
22594
+ }
22595
+ resolve(doc) {
22596
+ return TextSelection.between(doc.resolve(this.anchor), doc.resolve(this.head));
22597
+ }
22598
+ }
22599
+ class NodeSelection extends Selection {
22600
+ /**
22601
+ Create a node selection. Does not verify the validity of its
22602
+ argument.
22603
+ */
22604
+ constructor($pos) {
22605
+ let node = $pos.nodeAfter;
22606
+ let $end = $pos.node(0).resolve($pos.pos + node.nodeSize);
22607
+ super($pos, $end);
22608
+ this.node = node;
22609
+ }
22610
+ map(doc, mapping) {
22611
+ let { deleted, pos } = mapping.mapResult(this.anchor);
22612
+ let $pos = doc.resolve(pos);
22613
+ if (deleted)
22614
+ return Selection.near($pos);
22615
+ return new NodeSelection($pos);
22616
+ }
22617
+ content() {
22618
+ return new Slice(Fragment.from(this.node), 0, 0);
22619
+ }
22620
+ eq(other) {
22621
+ return other instanceof NodeSelection && other.anchor == this.anchor;
22622
+ }
22623
+ toJSON() {
22624
+ return { type: "node", anchor: this.anchor };
22625
+ }
22626
+ getBookmark() {
22627
+ return new NodeBookmark(this.anchor);
22628
+ }
22629
+ /**
22630
+ @internal
22631
+ */
22632
+ static fromJSON(doc, json) {
22633
+ if (typeof json.anchor != "number")
22634
+ throw new RangeError("Invalid input for NodeSelection.fromJSON");
22635
+ return new NodeSelection(doc.resolve(json.anchor));
22636
+ }
22637
+ /**
22638
+ Create a node selection from non-resolved positions.
22639
+ */
22640
+ static create(doc, from) {
22641
+ return new NodeSelection(doc.resolve(from));
22642
+ }
22643
+ /**
22644
+ Determines whether the given node may be selected as a node
22645
+ selection.
22646
+ */
22647
+ static isSelectable(node) {
22648
+ return !node.isText && node.type.spec.selectable !== false;
22649
+ }
22650
+ }
22651
+ NodeSelection.prototype.visible = false;
22652
+ Selection.jsonID("node", NodeSelection);
22653
+ class NodeBookmark {
22654
+ constructor(anchor) {
22655
+ this.anchor = anchor;
22656
+ }
22657
+ map(mapping) {
22658
+ let { deleted, pos } = mapping.mapResult(this.anchor);
22659
+ return deleted ? new TextBookmark(pos, pos) : new NodeBookmark(pos);
22660
+ }
22661
+ resolve(doc) {
22662
+ let $pos = doc.resolve(this.anchor), node = $pos.nodeAfter;
22663
+ if (node && NodeSelection.isSelectable(node))
22664
+ return new NodeSelection($pos);
22665
+ return Selection.near($pos);
22666
+ }
22667
+ }
22668
+ class AllSelection extends Selection {
22669
+ /**
22670
+ Create an all-selection over the given document.
22671
+ */
22672
+ constructor(doc) {
22673
+ super(doc.resolve(0), doc.resolve(doc.content.size));
22674
+ }
22675
+ replace(tr, content = Slice.empty) {
22676
+ if (content == Slice.empty) {
22677
+ tr.delete(0, tr.doc.content.size);
22678
+ let sel = Selection.atStart(tr.doc);
22679
+ if (!sel.eq(tr.selection))
22680
+ tr.setSelection(sel);
22681
+ } else {
22682
+ super.replace(tr, content);
22683
+ }
22684
+ }
22685
+ toJSON() {
22686
+ return { type: "all" };
22687
+ }
22688
+ /**
22689
+ @internal
22690
+ */
22691
+ static fromJSON(doc) {
22692
+ return new AllSelection(doc);
22693
+ }
22694
+ map(doc) {
22695
+ return new AllSelection(doc);
22696
+ }
22697
+ eq(other) {
22698
+ return other instanceof AllSelection;
22699
+ }
22700
+ getBookmark() {
22701
+ return AllBookmark;
22702
+ }
22703
+ }
22704
+ Selection.jsonID("all", AllSelection);
22705
+ const AllBookmark = {
22706
+ map() {
22707
+ return this;
22806
22708
  },
22807
- addKeyboardShortcuts() {
22808
- return {
22809
- Backspace: () => {
22810
- const { selection, doc } = this.editor.state;
22811
- const $pos = selection.$anchor;
22812
- const nodeBefore = $pos.nodeBefore;
22813
- if (nodeBefore?.type.name === this.name && nodeBefore.attrs.uploading) {
22814
- toast.info("Please wait for upload to complete or cancel it first");
22815
- return true;
22816
- }
22817
- const nodeAtSelection = doc.nodeAt(selection.from);
22818
- if (nodeAtSelection?.type.name === this.name && nodeAtSelection.attrs.uploading) {
22819
- toast.info("Please wait for upload to complete or cancel it first");
22820
- return true;
22821
- }
22822
- return false;
22823
- },
22824
- Delete: () => {
22825
- const { selection, doc } = this.editor.state;
22826
- const $pos = selection.$anchor;
22827
- const nodeAfter = $pos.nodeAfter;
22828
- if (nodeAfter?.type.name === this.name && nodeAfter.attrs.uploading) {
22829
- toast.info("Please wait for upload to complete or cancel it first");
22830
- return true;
22831
- }
22832
- const nodeAtSelection = doc.nodeAt(selection.from);
22833
- if (nodeAtSelection?.type.name === this.name && nodeAtSelection.attrs.uploading) {
22834
- toast.info("Please wait for upload to complete or cancel it first");
22835
- return true;
22836
- }
22837
- return false;
22838
- }
22839
- };
22709
+ resolve(doc) {
22710
+ return new AllSelection(doc);
22840
22711
  }
22841
- });
22842
- const buildWrapperStyle = (align) => `width: 100%; display: flex; justify-content: ${getAlignmentStyle(align)}; margin:16px 0`;
22843
- const buildMediaStyle = (baseStyle, type, maxWidth) => `${baseStyle}; max-width: ${type === MediaNodeTypes.Audio ? "100%" : `${maxWidth}%`}; ${type !== MediaNodeTypes.Audio ? "height: auto;" : ""}`;
22844
- const getAlignmentStyle = (align) => {
22845
- const map = {
22846
- [Alignment.LEFT]: "flex-start",
22847
- [Alignment.CENTER]: "center",
22848
- [Alignment.RIGHT]: "flex-end"
22849
- };
22850
- return map[align] || "flex-start";
22851
22712
  };
22713
+ function findSelectionIn(doc, node, pos, index2, dir, text2 = false) {
22714
+ if (node.inlineContent)
22715
+ return TextSelection.create(doc, pos);
22716
+ for (let i = index2 - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
22717
+ let child = node.child(i);
22718
+ if (!child.isAtom) {
22719
+ let inner = findSelectionIn(doc, child, pos + dir, dir < 0 ? child.childCount : 0, dir, text2);
22720
+ if (inner)
22721
+ return inner;
22722
+ } else if (!text2 && NodeSelection.isSelectable(child)) {
22723
+ return NodeSelection.create(doc, pos - (dir < 0 ? child.nodeSize : 0));
22724
+ }
22725
+ pos += child.nodeSize * dir;
22726
+ }
22727
+ return null;
22728
+ }
22729
+ function selectionToInsertionEnd(tr, startLen, bias) {
22730
+ let last2 = tr.steps.length - 1;
22731
+ if (last2 < startLen)
22732
+ return;
22733
+ let step = tr.steps[last2];
22734
+ if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep))
22735
+ return;
22736
+ let map = tr.mapping.maps[last2], end2;
22737
+ map.forEach((_from, _to, _newFrom, newTo) => {
22738
+ if (end2 == null)
22739
+ end2 = newTo;
22740
+ });
22741
+ tr.setSelection(Selection.near(tr.doc.resolve(end2), bias));
22742
+ }
22743
+ function bind(f, self2) {
22744
+ return !self2 || !f ? f : f.bind(self2);
22745
+ }
22746
+ class FieldDesc {
22747
+ constructor(name, desc, self2) {
22748
+ this.name = name;
22749
+ this.init = bind(desc.init, self2);
22750
+ this.apply = bind(desc.apply, self2);
22751
+ }
22752
+ }
22753
+ [
22754
+ new FieldDesc("doc", {
22755
+ init(config) {
22756
+ return config.doc || config.schema.topNodeType.createAndFill();
22757
+ },
22758
+ apply(tr) {
22759
+ return tr.doc;
22760
+ }
22761
+ }),
22762
+ new FieldDesc("selection", {
22763
+ init(config, instance) {
22764
+ return config.selection || Selection.atStart(instance.doc);
22765
+ },
22766
+ apply(tr) {
22767
+ return tr.selection;
22768
+ }
22769
+ }),
22770
+ new FieldDesc("storedMarks", {
22771
+ init(config) {
22772
+ return config.storedMarks || null;
22773
+ },
22774
+ apply(tr, _marks, _old, state2) {
22775
+ return state2.selection.$cursor ? tr.storedMarks : null;
22776
+ }
22777
+ }),
22778
+ new FieldDesc("scrollToSelection", {
22779
+ init() {
22780
+ return 0;
22781
+ },
22782
+ apply(tr, prev) {
22783
+ return tr.scrolledIntoView ? prev + 1 : prev;
22784
+ }
22785
+ })
22786
+ ];
22787
+ function bindProps(obj, self2, target) {
22788
+ for (let prop in obj) {
22789
+ let val = obj[prop];
22790
+ if (val instanceof Function)
22791
+ val = val.bind(self2);
22792
+ else if (prop == "handleDOMEvents")
22793
+ val = bindProps(val, self2, {});
22794
+ target[prop] = val;
22795
+ }
22796
+ return target;
22797
+ }
22798
+ class Plugin {
22799
+ /**
22800
+ Create a plugin.
22801
+ */
22802
+ constructor(spec) {
22803
+ this.spec = spec;
22804
+ this.props = {};
22805
+ if (spec.props)
22806
+ bindProps(spec.props, this, this.props);
22807
+ this.key = spec.key ? spec.key.key : createKey("plugin");
22808
+ }
22809
+ /**
22810
+ Extract the plugin's state field from an editor state.
22811
+ */
22812
+ getState(state2) {
22813
+ return state2[this.key];
22814
+ }
22815
+ }
22816
+ const keys = /* @__PURE__ */ Object.create(null);
22817
+ function createKey(name) {
22818
+ if (name in keys)
22819
+ return name + "$" + ++keys[name];
22820
+ keys[name] = 0;
22821
+ return name + "$";
22822
+ }
22823
+ class PluginKey {
22824
+ /**
22825
+ Create a plugin key.
22826
+ */
22827
+ constructor(name = "key") {
22828
+ this.key = createKey(name);
22829
+ }
22830
+ /**
22831
+ Get the active plugin with this key, if any, from an editor
22832
+ state.
22833
+ */
22834
+ get(state2) {
22835
+ return state2.config.pluginsByKey[this.key];
22836
+ }
22837
+ /**
22838
+ Get the plugin's state from an editor state.
22839
+ */
22840
+ getState(state2) {
22841
+ return state2[this.key];
22842
+ }
22843
+ }
22852
22844
  const TextChoiceCard = ({ choice, index: index2, canRemove, onRemove }) => {
22853
22845
  const backgroundColor = OPTION_COLORS[index2 % OPTION_COLORS.length];
22854
22846
  return /* @__PURE__ */ jsxRuntime.jsxs(