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