@pixerate/editor 0.234.0 → 0.236.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +286 -27
- package/dist/agent/index.d.cts +1 -0
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +261 -2
- package/dist/agent/reviewTools.d.cts +16 -0
- package/dist/agent/reviewTools.d.ts +16 -0
- package/dist/animation-editor/controller.d.cts +25 -0
- package/dist/animation-editor/controller.d.ts +25 -0
- package/dist/animation-editor/index.cjs +159 -50
- package/dist/animation-editor/index.d.cts +1 -0
- package/dist/animation-editor/index.d.ts +1 -0
- package/dist/animation-editor/index.js +115 -6
- package/dist/animation-editor/layers.d.cts +2 -0
- package/dist/animation-editor/layers.d.ts +2 -0
- package/dist/animation-editor/sequence.d.cts +33 -0
- package/dist/animation-editor/sequence.d.ts +33 -0
- package/dist/{chunk-PI2UYMZY.js → chunk-FSLKBQU7.js} +269 -15
- package/dist/{chunk-I277KJLQ.js → chunk-G3RUKJ5C.js} +105 -1
- package/dist/{chunk-6RZVFYGJ.cjs → chunk-JWCKJNDR.cjs} +105 -1
- package/dist/{chunk-3MO3ZS7E.js → chunk-PHYOZK3B.js} +59 -311
- package/dist/{chunk-CZBRY4SW.cjs → chunk-WPWHT4Z6.cjs} +122 -374
- package/dist/{chunk-O7QXVGMP.cjs → chunk-WV5WTOC6.cjs} +295 -41
- package/dist/effects/index.cjs +19 -11
- package/dist/effects/index.d.cts +1 -0
- package/dist/effects/index.d.ts +1 -0
- package/dist/effects/index.js +9 -1
- package/dist/effects/template-tools.d.cts +25 -0
- package/dist/effects/template-tools.d.ts +25 -0
- package/dist/effects/templates.d.cts +30 -1
- package/dist/effects/templates.d.ts +30 -1
- package/dist/extensions/index.cjs +6 -6
- package/dist/extensions/index.js +10 -10
- package/dist/index.cjs +11 -11
- package/dist/index.js +10 -10
- package/dist/video-editor/controller.d.cts +26 -0
- package/dist/video-editor/controller.d.ts +26 -0
- package/dist/video-editor/index.cjs +187 -42
- package/dist/video-editor/index.d.cts +1 -0
- package/dist/video-editor/index.d.ts +1 -0
- package/dist/video-editor/index.js +145 -0
- package/dist/video-editor/sequence.d.cts +38 -0
- package/dist/video-editor/sequence.d.ts +38 -0
- package/package.json +1 -1
- package/skills/pixerate-rich-text/SKILL.md +28 -1
- package/skills/pixerate-rich-text-revise/SKILL.md +8 -1
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
effectLayerInput,
|
|
3
|
-
effectLayerOffered,
|
|
4
|
-
effectLayerRole,
|
|
5
|
-
effectSubjectKind
|
|
6
|
-
} from "../chunk-I277KJLQ.js";
|
|
7
1
|
import {
|
|
8
2
|
createVectorMorph
|
|
9
3
|
} from "../chunk-YMREOA67.js";
|
|
@@ -15,6 +9,14 @@ import {
|
|
|
15
9
|
namedWithSuggestion,
|
|
16
10
|
suggestName
|
|
17
11
|
} from "../chunk-EUKY4KFX.js";
|
|
12
|
+
import {
|
|
13
|
+
effectLayerInput,
|
|
14
|
+
effectLayerOffered,
|
|
15
|
+
effectLayerRole,
|
|
16
|
+
effectSubjectKind,
|
|
17
|
+
effectTemplateTools,
|
|
18
|
+
fillEffectTemplate
|
|
19
|
+
} from "../chunk-G3RUKJ5C.js";
|
|
18
20
|
import {
|
|
19
21
|
IDENTITY_MATRIX,
|
|
20
22
|
applyMatrix,
|
|
@@ -1802,7 +1804,10 @@ var MotionLayersEngine = class {
|
|
|
1802
1804
|
this.reset();
|
|
1803
1805
|
this.listeners.clear();
|
|
1804
1806
|
}
|
|
1807
|
+
/** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
|
|
1808
|
+
revision = 0;
|
|
1805
1809
|
notify() {
|
|
1810
|
+
this.revision++;
|
|
1806
1811
|
this.listeners.forEach((l) => l());
|
|
1807
1812
|
}
|
|
1808
1813
|
size(b) {
|
|
@@ -4484,6 +4489,57 @@ async function checkMotionShotLayers(scene, provider) {
|
|
|
4484
4489
|
return issues;
|
|
4485
4490
|
}
|
|
4486
4491
|
|
|
4492
|
+
// src/animation-editor/sequence.ts
|
|
4493
|
+
var fresh = (used, base) => {
|
|
4494
|
+
const clean = base.replace(/[^\w.-]+/g, "-").replace(/^[^A-Za-z_]+/, "") || "item";
|
|
4495
|
+
if (!used.has(clean)) return used.add(clean), clean;
|
|
4496
|
+
for (let i = 2; ; i++) if (!used.has(`${clean}-${i}`)) return used.add(`${clean}-${i}`), `${clean}-${i}`;
|
|
4497
|
+
};
|
|
4498
|
+
function motionShotsFromSequence(sequence, options = {}) {
|
|
4499
|
+
const sx = (options.width ?? sequence.stage.width) / sequence.stage.width;
|
|
4500
|
+
const sy = (options.height ?? sequence.stage.height) / sequence.stage.height;
|
|
4501
|
+
const scale = (r) => ({ x: r.x * sx, y: r.y * sy, width: r.width * sx, height: r.height * sy });
|
|
4502
|
+
const full = { x: 0, y: 0, width: sequence.stage.width * sx, height: sequence.stage.height * sy };
|
|
4503
|
+
const shotIds = new Set(options.shotIds ?? []);
|
|
4504
|
+
const layerIds = new Set(options.layerIds ?? []);
|
|
4505
|
+
const notes = [];
|
|
4506
|
+
const provider = options.provider ? { provider: options.provider } : {};
|
|
4507
|
+
const shots = sequence.shots.map((s, i) => {
|
|
4508
|
+
const id = fresh(shotIds, `shot${i + 1}`);
|
|
4509
|
+
const framing = s.camera?.framing ? scale(s.camera.framing) : void 0;
|
|
4510
|
+
const view = framing ?? full;
|
|
4511
|
+
const layers = [];
|
|
4512
|
+
for (const layer of s.layers) {
|
|
4513
|
+
if (layer.subject && "video" in layer.subject) {
|
|
4514
|
+
notes.push(`Shot ${i + 1}: a layer plays a video, which an animation cannot; it was left out (the video editor plays it).`);
|
|
4515
|
+
continue;
|
|
4516
|
+
}
|
|
4517
|
+
const { id: given, rect, subject, ...rest } = layer;
|
|
4518
|
+
layers.push({ ...rest, id: fresh(layerIds, given ?? "layer"), ...subject ? { subject } : {}, ...rect ? { rect: scale(rect) } : {}, ...layer.effect ? provider : {} });
|
|
4519
|
+
}
|
|
4520
|
+
if (s.title) layers.push({ id: fresh(layerIds, `${id}-title`), subject: { text: s.title }, role: "overlay", rect: { x: view.x + view.width * 0.1, y: view.y + view.height * 0.38, width: view.width * 0.8, height: view.height * 0.24 } });
|
|
4521
|
+
if (s.caption) layers.push({ id: fresh(layerIds, `${id}-caption`), subject: { text: s.caption }, role: "overlay", rect: { x: view.x + view.width * 0.1, y: view.y + view.height * 0.8, width: view.width * 0.8, height: view.height * 0.1 } });
|
|
4522
|
+
const camera = framing || s.camera?.in !== void 0 || s.camera?.during !== void 0 ? { ...framing ?? {}, ...s.camera?.in !== void 0 ? { in: s.camera.in } : {}, ...s.camera?.during !== void 0 ? { during: s.camera.during } : {} } : void 0;
|
|
4523
|
+
return {
|
|
4524
|
+
id,
|
|
4525
|
+
label: s.title ?? s.caption ?? `Shot ${i + 1}`,
|
|
4526
|
+
...camera ? { camera } : {},
|
|
4527
|
+
run: [],
|
|
4528
|
+
hold: s.hold,
|
|
4529
|
+
...layers.length ? { layers } : {},
|
|
4530
|
+
...i > 0 && s.transition ? { transition: { ...s.transition, ...provider } } : {}
|
|
4531
|
+
};
|
|
4532
|
+
});
|
|
4533
|
+
return { shots, notes };
|
|
4534
|
+
}
|
|
4535
|
+
function motionSceneFromSequence(sequence, options = {}) {
|
|
4536
|
+
const { shots, notes } = motionShotsFromSequence(sequence, options);
|
|
4537
|
+
return {
|
|
4538
|
+
scene: { version: 1, name: sequence.name, ...sequence.title ? { description: sequence.title } : {}, artwork: { width: sequence.stage.width, height: sequence.stage.height, background: options.background ?? "#000000", nodes: [] }, shots },
|
|
4539
|
+
notes
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
|
|
4487
4543
|
// src/animation-editor/effects.ts
|
|
4488
4544
|
var sourceOf = (c) => c.source;
|
|
4489
4545
|
function motionSceneEffects(scene) {
|
|
@@ -4667,6 +4723,7 @@ var AnimationEditorController = class {
|
|
|
4667
4723
|
redoLabel: h.redoName,
|
|
4668
4724
|
revision: this.revision,
|
|
4669
4725
|
effectsProvider: this.effects.provider,
|
|
4726
|
+
layerPictures: this.layers.revision,
|
|
4670
4727
|
recording: this.recording ? {
|
|
4671
4728
|
shot: this.recording.options.id,
|
|
4672
4729
|
at: this.recording.options.at,
|
|
@@ -5175,6 +5232,42 @@ var AnimationEditorController = class {
|
|
|
5175
5232
|
if (!r.ok) return r;
|
|
5176
5233
|
return this.layerResult(shot, void 0);
|
|
5177
5234
|
}
|
|
5235
|
+
/**
|
|
5236
|
+
* Fills a provider sequence template and loads it: `replace` (default) makes a new scene the
|
|
5237
|
+
* size of the template's stage from its shots; `append` adds its shots after the current ones,
|
|
5238
|
+
* scaled to the artwork. Values are checked first and every problem is refused at once. One
|
|
5239
|
+
* undo step. Returns the new shot and layer ids, slots left open and notes.
|
|
5240
|
+
*/
|
|
5241
|
+
async fillTemplate(template, values = {}, options = {}) {
|
|
5242
|
+
const reason = this.readOnlyReason();
|
|
5243
|
+
if (reason) return this.refuse(reason);
|
|
5244
|
+
const mode = options.mode ?? "replace";
|
|
5245
|
+
let filled;
|
|
5246
|
+
try {
|
|
5247
|
+
filled = await fillEffectTemplate(this.effects.provider, template, values, options.name ? { name: options.name } : {});
|
|
5248
|
+
} catch (error) {
|
|
5249
|
+
if (isEffectError(error) || error instanceof Error) return this.refuse(error.message);
|
|
5250
|
+
throw error;
|
|
5251
|
+
}
|
|
5252
|
+
const provider = this.effects.provider.id;
|
|
5253
|
+
const label = `Template ${filled.template.name}`;
|
|
5254
|
+
let shots;
|
|
5255
|
+
let notes;
|
|
5256
|
+
let r;
|
|
5257
|
+
if (mode === "replace") {
|
|
5258
|
+
const made = motionSceneFromSequence(filled.sequence, { provider });
|
|
5259
|
+
shots = made.scene.shots;
|
|
5260
|
+
notes = made.notes;
|
|
5261
|
+
r = this.apply({ op: "replace_scene", scene: made.scene }, label);
|
|
5262
|
+
} else {
|
|
5263
|
+
const made = motionShotsFromSequence(filled.sequence, { provider, width: this.scene.artwork.width, height: this.scene.artwork.height, shotIds: this.scene.shots.map((s) => s.id), layerIds: this.scene.shots.flatMap((s) => (s.layers ?? []).map((l) => l.id)) });
|
|
5264
|
+
shots = made.shots;
|
|
5265
|
+
notes = made.notes;
|
|
5266
|
+
r = this.apply({ op: "insert_shots", shots: made.shots }, label);
|
|
5267
|
+
}
|
|
5268
|
+
if (!r.ok) return r;
|
|
5269
|
+
return { ok: true, template, mode, shots: shots.map((s) => s.id), layers: shots.flatMap((s) => (s.layers ?? []).map((l) => l.id)), openSlots: filled.openSlots, notes: [...filled.notes, ...notes] };
|
|
5270
|
+
}
|
|
5178
5271
|
// ---------------------------------------------------------------------------------------
|
|
5179
5272
|
// Recording
|
|
5180
5273
|
// ---------------------------------------------------------------------------------------
|
|
@@ -7087,6 +7180,20 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
|
|
|
7087
7180
|
extraTools: (ctx) => [
|
|
7088
7181
|
...animationCameraMoveTools(controller, ctx),
|
|
7089
7182
|
...animationShotLayerTools(controller, ctx),
|
|
7183
|
+
...effectTemplateTools(
|
|
7184
|
+
{
|
|
7185
|
+
provider: () => controller.effectsProvider,
|
|
7186
|
+
fillDetails: "mode replace (default) makes a new scene from the template's shots, the size of its stage; mode append adds its shots after the current ones, scaled to the artwork. Each shot keeps its framing and camera moves, its layers and its transition; its title and caption become overlay word layers. Returns { template, mode, shots (new shot ids), layers (new layer ids), openSlots, notes }. Check the result with render_animation_frame.",
|
|
7187
|
+
fillOptions: { mode: { type: "string", enum: ["replace", "append"], description: "replace (default): a new scene from the template; append: its shots after the current ones." } },
|
|
7188
|
+
fill: async (template, values, options2) => {
|
|
7189
|
+
const r = await controller.fillTemplate(template, values, { name: options2.name, mode: options2.mode });
|
|
7190
|
+
if (!r.ok) throw new Error(r.error);
|
|
7191
|
+
const { ok: _ok, ...rest } = r;
|
|
7192
|
+
return rest;
|
|
7193
|
+
}
|
|
7194
|
+
},
|
|
7195
|
+
ctx
|
|
7196
|
+
),
|
|
7090
7197
|
{
|
|
7091
7198
|
name: "add_vector_effect",
|
|
7092
7199
|
title: "Add a vector effect",
|
|
@@ -7296,6 +7403,7 @@ export {
|
|
|
7296
7403
|
motionRevealPadding,
|
|
7297
7404
|
motionRulerTicks,
|
|
7298
7405
|
motionSceneEffects,
|
|
7406
|
+
motionSceneFromSequence,
|
|
7299
7407
|
motionSceneHasLayers,
|
|
7300
7408
|
motionSceneShapeProblems,
|
|
7301
7409
|
motionSceneToJson,
|
|
@@ -7304,6 +7412,7 @@ export {
|
|
|
7304
7412
|
motionShotCueTime,
|
|
7305
7413
|
motionShotLayersInfo,
|
|
7306
7414
|
motionShotTimeScale,
|
|
7415
|
+
motionShotsFromSequence,
|
|
7307
7416
|
motionStageSvg,
|
|
7308
7417
|
motionTakeProblems,
|
|
7309
7418
|
motionTakeShot,
|
|
@@ -144,6 +144,8 @@ export declare class MotionLayersEngine implements MotionLayerPictures {
|
|
|
144
144
|
/** Forgets every picture and failure (a new provider, or the host changed). */
|
|
145
145
|
reset(): void;
|
|
146
146
|
dispose(): void;
|
|
147
|
+
/** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
|
|
148
|
+
revision: number;
|
|
147
149
|
private notify;
|
|
148
150
|
private size;
|
|
149
151
|
private snap;
|
|
@@ -144,6 +144,8 @@ export declare class MotionLayersEngine implements MotionLayerPictures {
|
|
|
144
144
|
/** Forgets every picture and failure (a new provider, or the host changed). */
|
|
145
145
|
reset(): void;
|
|
146
146
|
dispose(): void;
|
|
147
|
+
/** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
|
|
148
|
+
revision: number;
|
|
147
149
|
private notify;
|
|
148
150
|
private size;
|
|
149
151
|
private snap;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An effects provider's `EffectSequence` (what a sequence template fills to) as animation shots:
|
|
3
|
+
* each shot keeps its framing and camera moves, its layers and its transition. Its title and
|
|
4
|
+
* caption become overlay word layers placed in the shot's framing. Rects and framings are scaled
|
|
5
|
+
* from the sequence's stage to the artwork.
|
|
6
|
+
*/
|
|
7
|
+
import type { EffectSequence } from '../effects/types.cjs';
|
|
8
|
+
import type { MotionScene, MotionShot } from './types.cjs';
|
|
9
|
+
export interface MotionShotsFromSequence {
|
|
10
|
+
shots: MotionShot[];
|
|
11
|
+
/** What could not be carried over, in sentences. */
|
|
12
|
+
notes: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface MotionSequenceOptions {
|
|
15
|
+
/** The artwork size the shots are for (default the sequence's stage). */
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Shot ids already used (new ones avoid them). */
|
|
19
|
+
shotIds?: Iterable<string>;
|
|
20
|
+
/** Layer ids already used (new ones avoid them). */
|
|
21
|
+
layerIds?: Iterable<string>;
|
|
22
|
+
/** The provider the sequence's effects come from, saved on layers and transitions. */
|
|
23
|
+
provider?: string;
|
|
24
|
+
}
|
|
25
|
+
/** The shots of a sequence, sized for the artwork (see the module comment). */
|
|
26
|
+
export declare function motionShotsFromSequence(sequence: EffectSequence, options?: MotionSequenceOptions): MotionShotsFromSequence;
|
|
27
|
+
/** A new scene from a sequence: an empty artwork the size of its stage, and its shots. */
|
|
28
|
+
export declare function motionSceneFromSequence(sequence: EffectSequence, options?: Pick<MotionSequenceOptions, 'provider'> & {
|
|
29
|
+
background?: string;
|
|
30
|
+
}): {
|
|
31
|
+
scene: MotionScene;
|
|
32
|
+
notes: string[];
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An effects provider's `EffectSequence` (what a sequence template fills to) as animation shots:
|
|
3
|
+
* each shot keeps its framing and camera moves, its layers and its transition. Its title and
|
|
4
|
+
* caption become overlay word layers placed in the shot's framing. Rects and framings are scaled
|
|
5
|
+
* from the sequence's stage to the artwork.
|
|
6
|
+
*/
|
|
7
|
+
import type { EffectSequence } from '../effects/types.js';
|
|
8
|
+
import type { MotionScene, MotionShot } from './types.js';
|
|
9
|
+
export interface MotionShotsFromSequence {
|
|
10
|
+
shots: MotionShot[];
|
|
11
|
+
/** What could not be carried over, in sentences. */
|
|
12
|
+
notes: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface MotionSequenceOptions {
|
|
15
|
+
/** The artwork size the shots are for (default the sequence's stage). */
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Shot ids already used (new ones avoid them). */
|
|
19
|
+
shotIds?: Iterable<string>;
|
|
20
|
+
/** Layer ids already used (new ones avoid them). */
|
|
21
|
+
layerIds?: Iterable<string>;
|
|
22
|
+
/** The provider the sequence's effects come from, saved on layers and transitions. */
|
|
23
|
+
provider?: string;
|
|
24
|
+
}
|
|
25
|
+
/** The shots of a sequence, sized for the artwork (see the module comment). */
|
|
26
|
+
export declare function motionShotsFromSequence(sequence: EffectSequence, options?: MotionSequenceOptions): MotionShotsFromSequence;
|
|
27
|
+
/** A new scene from a sequence: an empty artwork the size of its stage, and its shots. */
|
|
28
|
+
export declare function motionSceneFromSequence(sequence: EffectSequence, options?: Pick<MotionSequenceOptions, 'provider'> & {
|
|
29
|
+
background?: string;
|
|
30
|
+
}): {
|
|
31
|
+
scene: MotionScene;
|
|
32
|
+
notes: string[];
|
|
33
|
+
};
|
|
@@ -17,21 +17,267 @@ import {
|
|
|
17
17
|
plainTextToSlice
|
|
18
18
|
} from "./chunk-2INPJDNY.js";
|
|
19
19
|
import {
|
|
20
|
+
CommentStore,
|
|
20
21
|
TRACK_CHANGES_SKIP_META,
|
|
22
|
+
createReviewId,
|
|
21
23
|
isTrackChangesEnabled,
|
|
22
24
|
safeMergeAttributes
|
|
23
25
|
} from "./chunk-6IN474P4.js";
|
|
24
26
|
|
|
27
|
+
// src/extensions/comments.ts
|
|
28
|
+
import { Mark } from "@tiptap/core";
|
|
29
|
+
import { Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
|
|
30
|
+
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
31
|
+
var commentsPluginKey = new PluginKey("comments");
|
|
32
|
+
var COMMENT_MARK_NAME = "comment";
|
|
33
|
+
function findCommentRanges(doc) {
|
|
34
|
+
const result = /* @__PURE__ */ new Map();
|
|
35
|
+
doc.descendants((node, pos) => {
|
|
36
|
+
if (!node.isInline) return;
|
|
37
|
+
node.marks.forEach((mark) => {
|
|
38
|
+
if (mark.type.name !== COMMENT_MARK_NAME) return;
|
|
39
|
+
const id = mark.attrs.commentId;
|
|
40
|
+
if (!id) return;
|
|
41
|
+
const ranges = result.get(id) ?? [];
|
|
42
|
+
const last = ranges[ranges.length - 1];
|
|
43
|
+
const end = pos + node.nodeSize;
|
|
44
|
+
if (last && last.to === pos) {
|
|
45
|
+
last.to = end;
|
|
46
|
+
} else {
|
|
47
|
+
ranges.push({ from: pos, to: end });
|
|
48
|
+
}
|
|
49
|
+
result.set(id, ranges);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function getCommentAnchors(doc) {
|
|
55
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
56
|
+
findCommentRanges(doc).forEach((ranges, id) => {
|
|
57
|
+
const from = ranges[0].from;
|
|
58
|
+
const to = ranges[ranges.length - 1].to;
|
|
59
|
+
anchors.set(id, {
|
|
60
|
+
from,
|
|
61
|
+
to,
|
|
62
|
+
quote: ranges.map((r) => doc.textBetween(r.from, r.to, " ", " ")).join(" ")
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
return anchors;
|
|
66
|
+
}
|
|
67
|
+
function getCommentIdsAt(state, pos = state.selection.from) {
|
|
68
|
+
const $pos = state.doc.resolve(pos);
|
|
69
|
+
const marks = [
|
|
70
|
+
...$pos.nodeAfter?.marks ?? [],
|
|
71
|
+
...$pos.nodeBefore?.marks ?? []
|
|
72
|
+
];
|
|
73
|
+
const ids = [];
|
|
74
|
+
marks.forEach((m) => {
|
|
75
|
+
if (m.type.name === COMMENT_MARK_NAME && m.attrs.commentId && !ids.includes(m.attrs.commentId)) {
|
|
76
|
+
ids.push(m.attrs.commentId);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return ids;
|
|
80
|
+
}
|
|
81
|
+
function getCommentStore(editor) {
|
|
82
|
+
return editor?.storage?.[COMMENT_MARK_NAME]?.store ?? null;
|
|
83
|
+
}
|
|
84
|
+
function resolveAuthor(author) {
|
|
85
|
+
return typeof author === "function" ? author() : author;
|
|
86
|
+
}
|
|
87
|
+
var Comments = Mark.create({
|
|
88
|
+
name: COMMENT_MARK_NAME,
|
|
89
|
+
// Typing at the edge of a comment does not extend it.
|
|
90
|
+
inclusive: false,
|
|
91
|
+
// Allow several comment marks (different threads) on the same text.
|
|
92
|
+
excludes: "",
|
|
93
|
+
addOptions() {
|
|
94
|
+
return {
|
|
95
|
+
store: null,
|
|
96
|
+
author: { id: "anonymous", name: "Anonymous" },
|
|
97
|
+
HTMLAttributes: {},
|
|
98
|
+
commentClass: "pixerate-comment bg-amber-100 border-b-2 border-amber-400 dark:bg-amber-500/20 cursor-pointer",
|
|
99
|
+
activeClass: "pixerate-comment-active bg-amber-200 dark:bg-amber-500/40",
|
|
100
|
+
resolvedClass: "",
|
|
101
|
+
activateOnSelection: true
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
addStorage() {
|
|
105
|
+
return {
|
|
106
|
+
store: this.options.store ?? new CommentStore(),
|
|
107
|
+
// tiptap-markdown: comments are not part of the exported markdown.
|
|
108
|
+
markdown: { serialize: { open: "", close: "" }, parse: {} }
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
addAttributes() {
|
|
112
|
+
return {
|
|
113
|
+
commentId: {
|
|
114
|
+
default: null,
|
|
115
|
+
parseHTML: (el) => el.getAttribute("data-comment-id"),
|
|
116
|
+
renderHTML: (attrs) => attrs.commentId ? { "data-comment-id": attrs.commentId } : {}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
parseHTML() {
|
|
121
|
+
return [{ tag: "span[data-comment-id]" }];
|
|
122
|
+
},
|
|
123
|
+
renderHTML({ HTMLAttributes }) {
|
|
124
|
+
return ["span", safeMergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
125
|
+
},
|
|
126
|
+
addCommands() {
|
|
127
|
+
const store = () => this.storage.store;
|
|
128
|
+
return {
|
|
129
|
+
addComment: (options = {}) => ({ state, tr, dispatch }) => {
|
|
130
|
+
const { from, to, empty } = state.selection;
|
|
131
|
+
if (empty) return false;
|
|
132
|
+
if (dispatch) {
|
|
133
|
+
const id = options.id ?? createReviewId("comment");
|
|
134
|
+
tr.addMark(from, to, this.type.create({ commentId: id }));
|
|
135
|
+
tr.setMeta(commentsPluginKey, "refresh");
|
|
136
|
+
store().createThread({
|
|
137
|
+
id,
|
|
138
|
+
body: options.body ?? "",
|
|
139
|
+
author: options.author ?? resolveAuthor(this.options.author),
|
|
140
|
+
anchor: { from, to, quote: state.doc.textBetween(from, to, " ", " ") }
|
|
141
|
+
});
|
|
142
|
+
store().setActiveThread(id);
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
},
|
|
146
|
+
resolveComment: (id, resolved = true) => ({ tr, dispatch }) => {
|
|
147
|
+
if (!store().getThread(id)) return false;
|
|
148
|
+
if (dispatch) {
|
|
149
|
+
store().resolveThread(id, resolved, resolveAuthor(this.options.author));
|
|
150
|
+
tr.setMeta(commentsPluginKey, "refresh");
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
},
|
|
154
|
+
deleteComment: (id) => ({ state, tr, dispatch }) => {
|
|
155
|
+
const ranges = findCommentRanges(state.doc).get(id) ?? [];
|
|
156
|
+
if (ranges.length === 0 && !store().getThread(id)) return false;
|
|
157
|
+
if (dispatch) {
|
|
158
|
+
ranges.forEach(({ from, to }) => {
|
|
159
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
160
|
+
const mark = node.marks.find(
|
|
161
|
+
(m) => m.type === this.type && m.attrs.commentId === id
|
|
162
|
+
);
|
|
163
|
+
if (mark) {
|
|
164
|
+
tr.removeMark(Math.max(pos, from), Math.min(pos + node.nodeSize, to), mark);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
tr.setMeta(commentsPluginKey, "refresh");
|
|
169
|
+
store().deleteThread(id);
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
},
|
|
173
|
+
setActiveComment: (id) => ({ tr, dispatch }) => {
|
|
174
|
+
if (id !== null && !store().getThread(id)) return false;
|
|
175
|
+
if (dispatch) {
|
|
176
|
+
store().setActiveThread(id);
|
|
177
|
+
tr.setMeta(commentsPluginKey, "refresh");
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
},
|
|
181
|
+
focusComment: (id) => ({ state, tr, dispatch }) => {
|
|
182
|
+
const ranges = findCommentRanges(state.doc).get(id);
|
|
183
|
+
if (!ranges || ranges.length === 0) return false;
|
|
184
|
+
if (dispatch) {
|
|
185
|
+
const from = ranges[0].from;
|
|
186
|
+
const to = ranges[ranges.length - 1].to;
|
|
187
|
+
tr.setSelection(TextSelection.create(tr.doc, from, to)).scrollIntoView();
|
|
188
|
+
store().setActiveThread(id);
|
|
189
|
+
tr.setMeta(commentsPluginKey, "refresh");
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
addProseMirrorPlugins() {
|
|
196
|
+
const store = this.storage.store;
|
|
197
|
+
const options = this.options;
|
|
198
|
+
let cache = null;
|
|
199
|
+
const buildDecorations2 = (doc) => {
|
|
200
|
+
const snapshot = store.getSnapshot();
|
|
201
|
+
if (cache && cache.doc === doc && cache.snapshot === snapshot) return cache.set;
|
|
202
|
+
const decorations = [];
|
|
203
|
+
findCommentRanges(doc).forEach((ranges, id) => {
|
|
204
|
+
const thread = store.getThread(id);
|
|
205
|
+
const resolved = thread?.resolved ?? false;
|
|
206
|
+
const active = snapshot.activeThreadId === id;
|
|
207
|
+
const cls = [
|
|
208
|
+
resolved ? options.resolvedClass : options.commentClass,
|
|
209
|
+
active ? options.activeClass : ""
|
|
210
|
+
].filter(Boolean).join(" ");
|
|
211
|
+
if (!cls) return;
|
|
212
|
+
ranges.forEach(({ from, to }) => {
|
|
213
|
+
decorations.push(Decoration.inline(from, to, { class: cls }));
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
const set = DecorationSet.create(doc, decorations);
|
|
217
|
+
cache = { doc, snapshot, set };
|
|
218
|
+
return set;
|
|
219
|
+
};
|
|
220
|
+
return [
|
|
221
|
+
new Plugin({
|
|
222
|
+
key: commentsPluginKey,
|
|
223
|
+
props: {
|
|
224
|
+
decorations: (state) => buildDecorations2(state.doc)
|
|
225
|
+
},
|
|
226
|
+
view: (view) => {
|
|
227
|
+
let destroyed = false;
|
|
228
|
+
let scheduled = false;
|
|
229
|
+
const sync = () => store.syncAnchors(getCommentAnchors(view.state.doc));
|
|
230
|
+
sync();
|
|
231
|
+
const unsubscribe = store.subscribe(() => {
|
|
232
|
+
if (scheduled) return;
|
|
233
|
+
scheduled = true;
|
|
234
|
+
queueMicrotask(() => {
|
|
235
|
+
scheduled = false;
|
|
236
|
+
if (destroyed) return;
|
|
237
|
+
sync();
|
|
238
|
+
if (cache?.snapshot !== store.getSnapshot()) {
|
|
239
|
+
view.dispatch(
|
|
240
|
+
view.state.tr.setMeta(commentsPluginKey, "refresh").setMeta("addToHistory", false)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
update(view2, prevState) {
|
|
247
|
+
if (!prevState.doc.eq(view2.state.doc)) sync();
|
|
248
|
+
if (options.activateOnSelection && !prevState.selection.eq(view2.state.selection)) {
|
|
249
|
+
const ids = getCommentIdsAt(view2.state);
|
|
250
|
+
const active = store.getSnapshot().activeThreadId;
|
|
251
|
+
if (!active || !ids.includes(active)) {
|
|
252
|
+
store.setActiveThread(ids[0] ?? null);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
destroy() {
|
|
257
|
+
destroyed = true;
|
|
258
|
+
unsubscribe();
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
function getCommentAuthor(editor) {
|
|
267
|
+
const ext = editor?.extensionManager?.extensions.find((e) => e.name === COMMENT_MARK_NAME);
|
|
268
|
+
return ext ? resolveAuthor(ext.options.author) : null;
|
|
269
|
+
}
|
|
270
|
+
|
|
25
271
|
// src/extensions/table/nesting.ts
|
|
26
272
|
import { Fragment, Slice } from "@tiptap/pm/model";
|
|
27
|
-
import { Plugin, PluginKey, Selection } from "@tiptap/pm/state";
|
|
273
|
+
import { Plugin as Plugin2, PluginKey as PluginKey2, Selection } from "@tiptap/pm/state";
|
|
28
274
|
import {
|
|
29
275
|
CellSelection,
|
|
30
276
|
__pastedCells,
|
|
31
277
|
handlePaste as handleTablePaste
|
|
32
278
|
} from "@tiptap/pm/tables";
|
|
33
279
|
var ALLOW_NESTED_TABLES_META = "allowNestedTables";
|
|
34
|
-
var tableNestingPluginKey = new
|
|
280
|
+
var tableNestingPluginKey = new PluginKey2("tableNesting");
|
|
35
281
|
var roleOf = (node) => node.type.spec.tableRole;
|
|
36
282
|
var isTableNode = (node) => roleOf(node) === "table";
|
|
37
283
|
function outermostTableDepth($pos) {
|
|
@@ -171,7 +417,7 @@ var skip = (tr) => !tr.docChanged || tr.getMeta(ALLOW_NESTED_TABLES_META) === tr
|
|
|
171
417
|
// (nested) table back so the deletion can be reviewed.
|
|
172
418
|
!!tr.getMeta("appendedTransaction") || replacesDocument(tr);
|
|
173
419
|
function createTableNestingPlugin() {
|
|
174
|
-
return new
|
|
420
|
+
return new Plugin2({
|
|
175
421
|
key: tableNestingPluginKey,
|
|
176
422
|
props: {
|
|
177
423
|
handlePaste: (view, event, slice) => handleTablePasteWithoutNesting(view, event, slice)
|
|
@@ -1334,7 +1580,7 @@ function createChartNodeView(mount, renderStatic) {
|
|
|
1334
1580
|
|
|
1335
1581
|
// src/extensions/chart/chart-node.ts
|
|
1336
1582
|
import { Node } from "@tiptap/core";
|
|
1337
|
-
import { Plugin as
|
|
1583
|
+
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
|
1338
1584
|
|
|
1339
1585
|
// src/extensions/document-structure/utils.ts
|
|
1340
1586
|
import { createDocument } from "@tiptap/core";
|
|
@@ -1419,7 +1665,7 @@ function syncInitialContent(editor, sync) {
|
|
|
1419
1665
|
var STRUCTURE_PRIORITY = 45;
|
|
1420
1666
|
|
|
1421
1667
|
// src/extensions/chart/chart-node.ts
|
|
1422
|
-
var chartPluginKey = new
|
|
1668
|
+
var chartPluginKey = new PluginKey3("chartNode");
|
|
1423
1669
|
function getDocumentCharts(doc, typeName = "chart") {
|
|
1424
1670
|
const charts = [];
|
|
1425
1671
|
doc.descendants((node, pos) => {
|
|
@@ -1614,7 +1860,7 @@ var Chart = Node.create({
|
|
|
1614
1860
|
const { name } = this;
|
|
1615
1861
|
const { generateId } = this.options;
|
|
1616
1862
|
return [
|
|
1617
|
-
new
|
|
1863
|
+
new Plugin3({
|
|
1618
1864
|
key: chartPluginKey,
|
|
1619
1865
|
appendTransaction: (transactions, _oldState, newState) => {
|
|
1620
1866
|
if (!transactions.some((tr) => tr.docChanged)) return null;
|
|
@@ -2007,11 +2253,11 @@ function createTableStore(editor) {
|
|
|
2007
2253
|
// src/extensions/agent-caret.ts
|
|
2008
2254
|
import { Extension } from "@tiptap/core";
|
|
2009
2255
|
import { closeHistory } from "@tiptap/pm/history";
|
|
2010
|
-
import { Plugin as
|
|
2011
|
-
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
2256
|
+
import { Plugin as Plugin4, PluginKey as PluginKey4 } from "@tiptap/pm/state";
|
|
2257
|
+
import { Decoration as Decoration2, DecorationSet as DecorationSet2 } from "@tiptap/pm/view";
|
|
2012
2258
|
var DEFAULT_AGENT_CARET_COLOR = "#8b5cf6";
|
|
2013
2259
|
var DEFAULT_AGENT_CARET_NAME = "Agent";
|
|
2014
|
-
var agentCaretPluginKey = new
|
|
2260
|
+
var agentCaretPluginKey = new PluginKey4("agentCaret");
|
|
2015
2261
|
var clamp = (pos, size) => Math.max(0, Math.min(size, Math.round(pos)));
|
|
2016
2262
|
function caretWidget(caret) {
|
|
2017
2263
|
return (view) => {
|
|
@@ -2036,7 +2282,7 @@ function buildDecorations(state, carets) {
|
|
|
2036
2282
|
for (const caret of carets) {
|
|
2037
2283
|
for (const range of caret.changed) {
|
|
2038
2284
|
decorations.push(
|
|
2039
|
-
|
|
2285
|
+
Decoration2.inline(range.from, range.to, {
|
|
2040
2286
|
class: "pe-agent-changed",
|
|
2041
2287
|
"data-agent-id": caret.id,
|
|
2042
2288
|
style: `background-color:${tint(caret.color, 18)};`
|
|
@@ -2047,7 +2293,7 @@ function buildDecorations(state, carets) {
|
|
|
2047
2293
|
const to = Math.max(caret.anchor, caret.head);
|
|
2048
2294
|
if (from < to) {
|
|
2049
2295
|
decorations.push(
|
|
2050
|
-
|
|
2296
|
+
Decoration2.inline(from, to, {
|
|
2051
2297
|
class: "pe-agent-selection",
|
|
2052
2298
|
"data-agent-id": caret.id,
|
|
2053
2299
|
style: `background-color:${tint(caret.color, 28)};`
|
|
@@ -2055,20 +2301,20 @@ function buildDecorations(state, carets) {
|
|
|
2055
2301
|
);
|
|
2056
2302
|
}
|
|
2057
2303
|
decorations.push(
|
|
2058
|
-
|
|
2304
|
+
Decoration2.widget(caret.head, caretWidget(caret), {
|
|
2059
2305
|
key: `agent-caret:${caret.id}:${caret.name}:${caret.color}`,
|
|
2060
2306
|
side: 10,
|
|
2061
2307
|
ignoreSelection: true
|
|
2062
2308
|
})
|
|
2063
2309
|
);
|
|
2064
2310
|
}
|
|
2065
|
-
return
|
|
2311
|
+
return DecorationSet2.create(state.doc, decorations);
|
|
2066
2312
|
}
|
|
2067
2313
|
function createAgentCaretPlugin() {
|
|
2068
|
-
return new
|
|
2314
|
+
return new Plugin4({
|
|
2069
2315
|
key: agentCaretPluginKey,
|
|
2070
2316
|
state: {
|
|
2071
|
-
init: (_, state) => ({ carets: [], spans: {}, decorations:
|
|
2317
|
+
init: (_, state) => ({ carets: [], spans: {}, decorations: DecorationSet2.create(state.doc, []) }),
|
|
2072
2318
|
apply(tr, value, _old, state) {
|
|
2073
2319
|
const meta = tr.getMeta(agentCaretPluginKey);
|
|
2074
2320
|
if (!tr.docChanged && !meta) return value;
|
|
@@ -2217,6 +2463,14 @@ async function typeAgentText(editor, options) {
|
|
|
2217
2463
|
}
|
|
2218
2464
|
|
|
2219
2465
|
export {
|
|
2466
|
+
commentsPluginKey,
|
|
2467
|
+
COMMENT_MARK_NAME,
|
|
2468
|
+
findCommentRanges,
|
|
2469
|
+
getCommentAnchors,
|
|
2470
|
+
getCommentIdsAt,
|
|
2471
|
+
getCommentStore,
|
|
2472
|
+
Comments,
|
|
2473
|
+
getCommentAuthor,
|
|
2220
2474
|
ALLOW_NESTED_TABLES_META,
|
|
2221
2475
|
tableNestingPluginKey,
|
|
2222
2476
|
outermostTableDepth,
|