@witchcraft/editor 0.0.6 → 0.0.8
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/module.json +1 -1
- package/dist/runtime/components/CodeBlockThemePicker.vue +0 -1
- package/dist/runtime/components/Commands.vue +1 -1
- package/dist/runtime/components/Editor.vue +1 -2
- package/dist/runtime/composables/useEditor.d.ts +1 -1
- package/dist/runtime/demo/App.vue +0 -1
- package/dist/runtime/pm/features/Blockquote/Blockquote.d.ts +1 -1
- package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.d.vue.ts +26 -0
- package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.vue +7 -0
- package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.vue.d.ts +26 -0
- package/dist/runtime/pm/features/Blocks/components/ItemMenu.vue +1 -2
- package/dist/runtime/pm/features/Blocks/components/ItemNodeView.vue +0 -1
- package/dist/runtime/pm/features/Blocks/components/defaultItemMenu.d.ts +2 -2
- package/dist/runtime/pm/features/Blocks/composables/useNodeStates.d.ts +2 -2
- package/dist/runtime/pm/features/CodeBlock/CodeBlock.d.ts +1 -1
- package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.vue +0 -1
- package/dist/runtime/pm/features/CodeBlock/composables/useHighlightJsTheme.d.ts +3 -3
- package/dist/runtime/pm/features/CommandsMenus/components/CommandBar.vue +1 -1
- package/dist/runtime/pm/features/CommandsMenus/components/CommandBarItem.vue +1 -1
- package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.vue +0 -1
- package/dist/runtime/pm/features/CommandsMenus/icons/HighlightIcon.vue +1 -1
- package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +0 -1
- package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.vue +0 -1
- package/dist/runtime/pm/features/FileLoader/components/FileLoaderNodeView.vue +0 -1
- package/dist/runtime/pm/features/HardBreak/HardBreak.d.ts +1 -1
- package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.vue +1 -1
- package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.vue +1 -1
- package/dist/runtime/pm/features/Link/components/BubbleMenuLink.vue +1 -1
- package/dist/runtime/pm/features/Menus/components/MarkMenuManager.vue +1 -1
- package/dist/runtime/pm/features/Tables/index.d.ts +5 -5
- package/dist/runtime/pm/generator.d.ts +82 -0
- package/dist/runtime/pm/generator.js +205 -0
- package/dist/runtime/pm/testSchema.d.ts +1 -1
- package/dist/runtime/pm/utils/generateRandomDoc.d.ts +23 -0
- package/dist/runtime/pm/utils/generateRandomDoc.js +83 -0
- package/dist/runtime/pm/utils/generateRandomTree.d.ts +50 -0
- package/dist/runtime/pm/utils/generateRandomTree.js +38 -0
- package/package.json +7 -5
- package/src/module.ts +1 -0
- package/src/runtime/components/CodeBlockThemePicker.vue +0 -1
- package/src/runtime/components/Editor.vue +0 -1
- package/src/runtime/demo/App.vue +0 -1
- package/src/runtime/pm/commands/changeAttrs.ts +1 -1
- package/src/runtime/pm/features/Blocks/Item.ts +1 -1
- package/src/runtime/pm/features/Blocks/commands/moveItem.ts +1 -1
- package/src/runtime/pm/features/Blocks/components/DragTreeHandle.vue +18 -14
- package/src/runtime/pm/features/Blocks/components/ItemMenu.vue +0 -1
- package/src/runtime/pm/features/Blocks/components/ItemNodeView.vue +0 -1
- package/src/runtime/pm/features/CodeBlock/components/CodeBlockView.vue +0 -1
- package/src/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.vue +0 -1
- package/src/runtime/pm/features/EmbeddedDocument/Embedded.ts +1 -1
- package/src/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +0 -1
- package/src/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.vue +0 -1
- package/src/runtime/pm/features/FileLoader/components/FileLoaderNodeView.vue +0 -1
- package/src/runtime/pm/features/FileLoader/types.ts +2 -2
- package/src/runtime/pm/generator.ts +266 -0
- package/src/runtime/pm/schema.ts +1 -0
- package/src/runtime/pm/utils/generateRandomDoc.ts +140 -0
- package/src/runtime/pm/utils/generateRandomTree.ts +100 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Node } from "@tiptap/pm/model";
|
|
2
|
+
import type { builders } from "prosemirror-test-builder";
|
|
3
|
+
import { generateRandomTree } from "./generateRandomTree.js";
|
|
4
|
+
import type { GeneratorConfigEntry } from "../generator.js";
|
|
5
|
+
/**
|
|
6
|
+
* Generates a random doc using faker js.
|
|
7
|
+
*
|
|
8
|
+
* A config describing how to create the doc is required. One is available under `pm/generator`.
|
|
9
|
+
*
|
|
10
|
+
* @experimental
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateRandomDoc<TBuilder extends ReturnType<typeof builders>, TData extends string>(builder: TBuilder, config?: GeneratorConfigEntry<any, any, any, any>[], treeOptions?: Parameters<typeof generateRandomTree>[1], { checkAfterNodeCreation }?: {
|
|
13
|
+
/**
|
|
14
|
+
* Checks if every returned node is valid for debugging invalid generator configs.
|
|
15
|
+
*
|
|
16
|
+
* A check is always done on the root node at the end regardless of this option.
|
|
17
|
+
*
|
|
18
|
+
* It is very slow (each check, rechecks children).
|
|
19
|
+
*
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
checkAfterNodeCreation?: boolean;
|
|
23
|
+
}): Node;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { unreachable } from "@alanscodelog/utils/unreachable";
|
|
2
|
+
import { faker } from "@faker-js/faker";
|
|
3
|
+
import { generateRandomTree } from "./generateRandomTree.js";
|
|
4
|
+
import { generatorConfig } from "../generator.js";
|
|
5
|
+
export function generateRandomDoc(builder, config = generatorConfig, treeOptions, {
|
|
6
|
+
checkAfterNodeCreation = false
|
|
7
|
+
} = {}) {
|
|
8
|
+
const schema = builder.schema;
|
|
9
|
+
const map = {};
|
|
10
|
+
function schemaFilter(name) {
|
|
11
|
+
return name !== "text" && name !== schema.topNodeType.name;
|
|
12
|
+
}
|
|
13
|
+
for (const entry of config) {
|
|
14
|
+
for (const node of entry.parents.names) {
|
|
15
|
+
if (node === "") throw new Error(`Empty node name ""`);
|
|
16
|
+
const type = entry.parents.type === "node" ? schema.nodes[node] : schema.marks[node];
|
|
17
|
+
if (type === void 0 && node !== "text") {
|
|
18
|
+
throw new Error(`${node} (on entry of type ${entry.parents.type}) not found in schema. Valid names are ${Object.keys(entry.parents.type === "node" ? schema.nodes : schema.marks)}`);
|
|
19
|
+
}
|
|
20
|
+
for (const node2 of entry.parents.names) {
|
|
21
|
+
const subEntry = { ...entry };
|
|
22
|
+
if (entry.ignoreValidation === void 0 || entry.ignoreValidation !== true) {
|
|
23
|
+
const childrenToValidate = (entry.children.names ?? []).filter((_) => !Array.isArray(entry.ignoreValidation) || !entry.ignoreValidation.includes(_));
|
|
24
|
+
for (const child of childrenToValidate) {
|
|
25
|
+
if (child === "") throw new Error(`Empty node name ""`);
|
|
26
|
+
if (entry.children.type === "node") {
|
|
27
|
+
const childType = schema.nodes[child];
|
|
28
|
+
const possibleNames = [childType.name, ...(childType.spec.group ?? "").split(" ").filter((e) => e !== "")];
|
|
29
|
+
const isAllowedChild = type.spec.content && possibleNames.some((n) => type.spec.content.includes(n));
|
|
30
|
+
if (!(isAllowedChild && schemaFilter(childType.name))) {
|
|
31
|
+
throw new Error(`Node ${node2} cannot contain child of type ${child}`);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
const childType = schema.marks[child];
|
|
35
|
+
if (entry.parents.type === "node") {
|
|
36
|
+
} else {
|
|
37
|
+
const possibleNames = [childType.name, ...(childType.spec.group ?? "").split(" ").filter((e) => e !== "")];
|
|
38
|
+
const t = type;
|
|
39
|
+
const isExcluded = t.spec.excludes !== void 0 && possibleNames.some((n) => {
|
|
40
|
+
return t.spec.excludes.includes(n) || t.spec.excludes.includes("_");
|
|
41
|
+
});
|
|
42
|
+
if (isExcluded) {
|
|
43
|
+
throw new Error(`Mark ${node2} cannot contain mark child of type ${child}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
map[node2] = { ...subEntry };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const children = generateRandomTree({
|
|
54
|
+
parentData: (data) => {
|
|
55
|
+
if (data === "") return "";
|
|
56
|
+
if (!data) unreachable();
|
|
57
|
+
if (!map[data] || !map[data].children.names || map[data].children.names.length === 0) {
|
|
58
|
+
return "";
|
|
59
|
+
} else {
|
|
60
|
+
const picked = faker.helpers.arrayElement(map[data].children.names);
|
|
61
|
+
return picked;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
createNode: (children2, _isLeaf, data) => {
|
|
65
|
+
if (data === "") return void 0;
|
|
66
|
+
if (!data) unreachable();
|
|
67
|
+
const type = schema.nodes[data];
|
|
68
|
+
if (!type) return void 0;
|
|
69
|
+
const parent = map[data]?.create?.(data, children2) ?? builder[data]({}, ...children2);
|
|
70
|
+
if (checkAfterNodeCreation) {
|
|
71
|
+
;
|
|
72
|
+
parent?.check?.();
|
|
73
|
+
}
|
|
74
|
+
return parent;
|
|
75
|
+
}
|
|
76
|
+
}, {
|
|
77
|
+
...treeOptions,
|
|
78
|
+
initialData: schema.topNodeType.name
|
|
79
|
+
});
|
|
80
|
+
const doc = map[schema.topNodeType.name]?.create?.(schema.topNodeType.name, children) ?? builder[schema.topNodeType.name]({}, ...children);
|
|
81
|
+
doc.check?.();
|
|
82
|
+
return doc;
|
|
83
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a random tree structure where the probability of generating children
|
|
3
|
+
* decreases linearly with depth, resulting in a tree that is bushy at the top
|
|
4
|
+
* and sparse towards the maximum depth.
|
|
5
|
+
*
|
|
6
|
+
* This function is agnostic to the actual node structure, relying on the caller-provided
|
|
7
|
+
* callbacks to create and link nodes.
|
|
8
|
+
*
|
|
9
|
+
* It uses faker.js for randomness (even on the option parameters).
|
|
10
|
+
*
|
|
11
|
+
* See {@link generateRandomDoc} for an example of how to use it.
|
|
12
|
+
*
|
|
13
|
+
* @experimental
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateRandomTree<T, TData>({ createNode, parentData }: {
|
|
16
|
+
/** A function that creates a new node (type T) given its depth. */
|
|
17
|
+
createNode: (children: T[], isLeaf: boolean, parentData?: TData) => T | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* A function that is called before creating a a node or it's children whose result is passed to both. This allows creating children that will be compatible with the parent (via this same function, it is passed it's parent).
|
|
20
|
+
*
|
|
21
|
+
* For example, we could randomly generate a parent type. It's then passed both to the children, to limit the types of children nodes, and to the parent so it can actually create it.
|
|
22
|
+
*/
|
|
23
|
+
parentData?: (parentData?: TData) => TData;
|
|
24
|
+
}, { rootNodes: rootNodes, depth, minChildren, maxInitialChildren, initialData }?: {
|
|
25
|
+
/**
|
|
26
|
+
* The exact number of nodes at depth 0.
|
|
27
|
+
*
|
|
28
|
+
* @default faker.number.int({ min: 0, max: 5 })
|
|
29
|
+
*/
|
|
30
|
+
rootNodes?: number;
|
|
31
|
+
/**
|
|
32
|
+
* The maximum depth of the tree (0-indexed). Nodes at this depth will have 0 children.
|
|
33
|
+
*
|
|
34
|
+
* @default faker.number.int({ min: 0, max: 5 })
|
|
35
|
+
*/
|
|
36
|
+
depth?: number;
|
|
37
|
+
/**
|
|
38
|
+
* The absolute minimum number of children any node can have.
|
|
39
|
+
*
|
|
40
|
+
* @default 0
|
|
41
|
+
*/
|
|
42
|
+
minChildren?: number;
|
|
43
|
+
/**
|
|
44
|
+
* The maximum children a node can have at depth 0. This value scales down as depth increases.
|
|
45
|
+
*
|
|
46
|
+
* @default 10
|
|
47
|
+
*/
|
|
48
|
+
maxInitialChildren?: number;
|
|
49
|
+
initialData?: TData;
|
|
50
|
+
}): T[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { faker } from "@faker-js/faker";
|
|
2
|
+
export function generateRandomTree({
|
|
3
|
+
createNode,
|
|
4
|
+
parentData
|
|
5
|
+
}, {
|
|
6
|
+
rootNodes = faker.number.int({ min: 0, max: 5 }),
|
|
7
|
+
depth = faker.number.int({ min: 0, max: 5 }),
|
|
8
|
+
minChildren = 0,
|
|
9
|
+
maxInitialChildren = 10,
|
|
10
|
+
initialData
|
|
11
|
+
} = {}) {
|
|
12
|
+
const generateChildren = (currentDepth, childCount, pData) => {
|
|
13
|
+
const res = [];
|
|
14
|
+
for (let i = 0; i < childCount; i++) {
|
|
15
|
+
const numChildren = calculateNumChildren(depth, currentDepth, minChildren, maxInitialChildren);
|
|
16
|
+
const data = parentData?.(pData) ?? void 0;
|
|
17
|
+
const parent = createNode(
|
|
18
|
+
generateChildren(currentDepth + 1, numChildren, data),
|
|
19
|
+
numChildren === 0,
|
|
20
|
+
data
|
|
21
|
+
);
|
|
22
|
+
if (parent === void 0) continue;
|
|
23
|
+
res.push(parent);
|
|
24
|
+
}
|
|
25
|
+
return res;
|
|
26
|
+
};
|
|
27
|
+
return generateChildren(0, rootNodes, initialData);
|
|
28
|
+
}
|
|
29
|
+
function calculateNumChildren(depth, currentDepth, minChildren, maxInitialChildren) {
|
|
30
|
+
const decayFactor = depth > 0 ? (depth - currentDepth) / depth : 0;
|
|
31
|
+
const maxAtDepth = minChildren + (maxInitialChildren - minChildren) * decayFactor;
|
|
32
|
+
const maxChildrenAtDepth = Math.max(minChildren, Math.floor(maxAtDepth));
|
|
33
|
+
let numChildren = 0;
|
|
34
|
+
if (currentDepth < depth) {
|
|
35
|
+
numChildren = faker.number.int({ min: minChildren, max: maxChildrenAtDepth });
|
|
36
|
+
}
|
|
37
|
+
return numChildren;
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@witchcraft/editor",
|
|
3
3
|
"description": "Block base prosemirror editor with partial/full editable document embeds, infinite embeds, and document uploads.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.8",
|
|
5
5
|
"main": "./dist/runtime/main.lib.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -110,11 +110,13 @@
|
|
|
110
110
|
"@alanscodelog/eslint-config": "^6.3.1",
|
|
111
111
|
"@alanscodelog/utils": "^6.0.2",
|
|
112
112
|
"@commitlint/cli": "^19.8.1",
|
|
113
|
+
"@faker-js/faker": "^10.0.0",
|
|
113
114
|
"@fortawesome/fontawesome-svg-core": "^7.0.1",
|
|
114
115
|
"@fortawesome/free-brands-svg-icons": "^7.0.1",
|
|
115
116
|
"@fortawesome/free-regular-svg-icons": "^7.0.1",
|
|
116
117
|
"@fortawesome/free-solid-svg-icons": "^7.0.1",
|
|
117
118
|
"@nuxt/eslint-config": "^1.9.0",
|
|
119
|
+
"@tiptap/html": "^3.4.2",
|
|
118
120
|
"@witchcraft/nuxt-utils": "^0.3.6",
|
|
119
121
|
"@witchcraft/ui": "^0.3.7",
|
|
120
122
|
"colord": "^2.9.3",
|
|
@@ -141,7 +143,7 @@
|
|
|
141
143
|
"@nuxt/module-builder": "^1.0.2",
|
|
142
144
|
"@nuxt/schema": "^4.1.2",
|
|
143
145
|
"@nuxt/types": "^2.18.1",
|
|
144
|
-
"@playwright/test": "
|
|
146
|
+
"@playwright/test": "=1.56.0",
|
|
145
147
|
"@rollup/plugin-dynamic-import-vars": "^2.1.5",
|
|
146
148
|
"@tailwindcss/cli": "^4.1.13",
|
|
147
149
|
"@tailwindcss/vite": "^4.1.13",
|
|
@@ -153,15 +155,15 @@
|
|
|
153
155
|
"@witchcraft/ui": "^0.3.7",
|
|
154
156
|
"concurrently": "^9.2.1",
|
|
155
157
|
"cross-env": "^10.0.0",
|
|
156
|
-
"eslint": "^9.
|
|
158
|
+
"eslint": "^9.38.0",
|
|
157
159
|
"fast-glob": "^3.3.3",
|
|
158
160
|
"http-server": "^14.1.1",
|
|
159
161
|
"husky": "^9.1.7",
|
|
160
162
|
"madge": "^8.0.0",
|
|
161
163
|
"nuxt": "^4.1.2",
|
|
162
164
|
"onchange": "^7.1.0",
|
|
163
|
-
"playwright": "
|
|
164
|
-
"playwright-core": "
|
|
165
|
+
"playwright": "=1.56.0",
|
|
166
|
+
"playwright-core": "=1.56.0",
|
|
165
167
|
"prosemirror-test-builder": "^1.1.1",
|
|
166
168
|
"radix-vue": "^1.9.17",
|
|
167
169
|
"semantic-release": "^24.2.8",
|
package/src/module.ts
CHANGED
|
@@ -89,6 +89,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|
|
89
89
|
nuxt.options.build.transpile.push("@tiptap/pm")
|
|
90
90
|
|
|
91
91
|
nuxt.hook("vite:extendConfig", config => {
|
|
92
|
+
// @ts-expect-error - optimizeDeps is now readonly but also possibly undefined :/
|
|
92
93
|
config.optimizeDeps ??= {}
|
|
93
94
|
config.optimizeDeps.exclude ??= []
|
|
94
95
|
// causes issues with the import.meta.globs here
|
package/src/runtime/demo/App.vue
CHANGED
|
@@ -32,7 +32,6 @@ declare module "@tiptap/core" {
|
|
|
32
32
|
export const Item = Node.create<ItemNodeOptions>({
|
|
33
33
|
name: "item" satisfies NodeItemName,
|
|
34
34
|
content: "block list? | list",
|
|
35
|
-
|
|
36
35
|
addOptions() {
|
|
37
36
|
return {
|
|
38
37
|
HTMLAttributes: {},
|
|
@@ -174,3 +173,4 @@ export const Item = Node.create<ItemNodeOptions>({
|
|
|
174
173
|
}
|
|
175
174
|
})
|
|
176
175
|
export type NodeItemName = "item"
|
|
176
|
+
|
|
@@ -97,27 +97,31 @@
|
|
|
97
97
|
</div>
|
|
98
98
|
</template>
|
|
99
99
|
|
|
100
|
-
<!--
|
|
101
|
-
Multipurpose drag handle + collapse indicator.
|
|
102
|
-
|
|
103
|
-
This is incredibly useful for making a compact draggable tree view.
|
|
104
|
-
|
|
105
|
-
The collapse indicator has a default height, but it should be set manually. For example `[&>.collapse-indicator]:h-[...]`
|
|
106
|
-
|
|
107
|
-
The component only emits a few events, it does not handle the dragging itself or what actually happens on any clicks/input.
|
|
108
|
-
-->
|
|
109
100
|
<script lang="ts">
|
|
101
|
+
/**
|
|
102
|
+
* Multipurpose drag handle + collapse indicator.
|
|
103
|
+
*
|
|
104
|
+
* This is incredibly useful for making a compact draggable tree view.
|
|
105
|
+
*
|
|
106
|
+
* The collapse indicator has a default height, but it should be set manually. For example `[&>.collapse-indicator]:h-[...]`
|
|
107
|
+
*
|
|
108
|
+
* The component only emits a few events, it does not handle the dragging itself or what actually happens on any clicks/input.
|
|
109
|
+
*/
|
|
110
|
+
interface Props {
|
|
111
|
+
hasChildren: boolean
|
|
112
|
+
passedDragThreshold: boolean
|
|
113
|
+
hideChildren: boolean
|
|
114
|
+
}
|
|
110
115
|
</script>
|
|
111
116
|
|
|
112
117
|
<script setup lang="ts">
|
|
113
118
|
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
114
119
|
import { onMounted, onUnmounted, ref, useAttrs } from "vue"
|
|
115
120
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
+
|
|
122
|
+
defineOptions({
|
|
123
|
+
name: "DragTreeHandle"
|
|
124
|
+
})
|
|
121
125
|
|
|
122
126
|
const $attrs = useAttrs()
|
|
123
127
|
defineProps<Props>()
|
|
@@ -46,7 +46,7 @@ export type IFileLoaderHandler<
|
|
|
46
46
|
editor: Editor,
|
|
47
47
|
pos: number | undefined,
|
|
48
48
|
error: Error,
|
|
49
|
-
loadingKey: TKey
|
|
49
|
+
loadingKey: TKey
|
|
50
50
|
) => void
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -77,7 +77,7 @@ export type IFileLoaderHandler<
|
|
|
77
77
|
editor: Editor,
|
|
78
78
|
pos: number,
|
|
79
79
|
res: T,
|
|
80
|
-
loadingKey: TKey
|
|
80
|
+
loadingKey: TKey
|
|
81
81
|
) => void
|
|
82
82
|
/**
|
|
83
83
|
* Return the file (or whatever type you'd like) to allow the extension to handle it.
|