@craftware/crafty-sdk 0.2.2 → 0.3.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/README.md +64 -0
- package/dist/attrs.d.ts +33 -0
- package/dist/attrs.d.ts.map +1 -0
- package/dist/attrs.js +52 -0
- package/dist/attrs.js.map +1 -0
- package/dist/cdn.d.ts +5 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +5 -0
- package/dist/cdn.js.map +1 -0
- package/dist/client.d.ts +12 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +13 -8
- package/dist/client.js.map +1 -1
- package/dist/crafty-visual-editing.global.js +4 -0
- package/dist/crafty-visual-editing.global.js.map +7 -0
- package/dist/http.d.ts +7 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +32 -2
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/preview.d.ts +56 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +174 -0
- package/dist/preview.js.map +1 -0
- package/dist/resources/collections.d.ts +2 -2
- package/dist/resources/collections.d.ts.map +1 -1
- package/dist/resources/entries.d.ts +3 -2
- package/dist/resources/entries.d.ts.map +1 -1
- package/dist/resources/entries.js +20 -4
- package/dist/resources/entries.js.map +1 -1
- package/dist/resources/forms.d.ts +2 -2
- package/dist/resources/forms.d.ts.map +1 -1
- package/dist/resources/forms.js +1 -1
- package/dist/resources/forms.js.map +1 -1
- package/dist/resources/mediaCollections.d.ts +2 -2
- package/dist/resources/mediaCollections.d.ts.map +1 -1
- package/dist/resources/mediaFiles.d.ts +2 -2
- package/dist/resources/mediaFiles.d.ts.map +1 -1
- package/dist/resources/pages.d.ts +2 -2
- package/dist/resources/pages.d.ts.map +1 -1
- package/dist/resources/pages.js +4 -0
- package/dist/resources/pages.js.map +1 -1
- package/dist/types/public.d.ts +11 -2
- package/dist/types/public.d.ts.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/visual-editing/bridge.d.ts +35 -0
- package/dist/visual-editing/bridge.d.ts.map +1 -0
- package/dist/visual-editing/bridge.js +385 -0
- package/dist/visual-editing/bridge.js.map +1 -0
- package/dist/visual-editing/index.d.ts +10 -0
- package/dist/visual-editing/index.d.ts.map +1 -0
- package/dist/visual-editing/index.js +10 -0
- package/dist/visual-editing/index.js.map +1 -0
- package/dist/visual-editing/live-preview.d.ts +15 -0
- package/dist/visual-editing/live-preview.d.ts.map +1 -0
- package/dist/visual-editing/live-preview.js +63 -0
- package/dist/visual-editing/live-preview.js.map +1 -0
- package/dist/visual-editing/markers.d.ts +11 -0
- package/dist/visual-editing/markers.d.ts.map +1 -0
- package/dist/visual-editing/markers.js +43 -0
- package/dist/visual-editing/markers.js.map +1 -0
- package/dist/visual-editing/origin.d.ts +10 -0
- package/dist/visual-editing/origin.d.ts.map +1 -0
- package/dist/visual-editing/origin.js +37 -0
- package/dist/visual-editing/origin.js.map +1 -0
- package/dist/visual-editing/overlay.d.ts +13 -0
- package/dist/visual-editing/overlay.d.ts.map +1 -0
- package/dist/visual-editing/overlay.js +107 -0
- package/dist/visual-editing/overlay.js.map +1 -0
- package/dist/visual-editing/protocol.d.ts +79 -0
- package/dist/visual-editing/protocol.d.ts.map +1 -0
- package/dist/visual-editing/protocol.js +150 -0
- package/dist/visual-editing/protocol.js.map +1 -0
- package/package.json +13 -4
- package/skills/crafty-cms/SKILL.md +14 -1
- package/skills/crafty-cms/references/mcp-configuration.md +7 -0
- package/skills/crafty-cms/references/sdk-development.md +13 -1
- package/skills/crafty-cms/references/visual-editor.md +218 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/visual-editing/origin.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,qBAAqB,GAAmC,CAAC,uCAAuC,CAAC,CAAC;AAE/G,SAAS,QAAQ,CAAC,KAAa;IAC7B,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,SAAyC;IAC5F,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAChC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC;QACtC,CAAC;QAED,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QAEtB,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,eAAgD,CAAC;IAChF,MAAM,KAAK,GAAG,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAEvC,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const FLASH_CLASS = "crafty-ve-flash";
|
|
2
|
+
export type Overlay = {
|
|
3
|
+
show(element: Element, label: string): void;
|
|
4
|
+
hide(): void;
|
|
5
|
+
/** Re-measures the highlighted element, e.g. after its content changed. */
|
|
6
|
+
reposition(): void;
|
|
7
|
+
flash(elements: Element[], durationMs: number): void;
|
|
8
|
+
ensureAttached(): void;
|
|
9
|
+
contains(node: Node | null): boolean;
|
|
10
|
+
destroy(): void;
|
|
11
|
+
};
|
|
12
|
+
export declare function createOverlay(doc: Document): Overlay;
|
|
13
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/visual-editing/overlay.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAU7C,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,IAAI,IAAI,IAAI,CAAC;IACb,2EAA2E;IAC3E,UAAU,IAAI,IAAI,CAAC;IACnB,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,cAAc,IAAI,IAAI,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAiHpD"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const STYLE_ELEMENT_ID = "crafty-visual-editing-style";
|
|
2
|
+
const ACCENT = "#4f46e5";
|
|
3
|
+
export const FLASH_CLASS = "crafty-ve-flash";
|
|
4
|
+
const STYLES = [
|
|
5
|
+
"[data-crafty-field]{cursor:pointer}",
|
|
6
|
+
`.crafty-ve-box{position:fixed;top:0;left:0;pointer-events:none;z-index:2147483647;box-sizing:border-box;border:2px solid ${ACCENT};border-radius:4px;background:rgba(79,70,229,.06)}`,
|
|
7
|
+
".crafty-ve-box[hidden]{display:none}",
|
|
8
|
+
`.crafty-ve-label{position:absolute;left:-2px;bottom:100%;margin-bottom:4px;padding:2px 8px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:4px;background:${ACCENT};color:#fff;font:500 12px/1.5 system-ui,-apple-system,sans-serif}`,
|
|
9
|
+
`.${FLASH_CLASS}{outline:2px dashed ${ACCENT} !important;outline-offset:2px !important}`
|
|
10
|
+
].join("\n");
|
|
11
|
+
export function createOverlay(doc) {
|
|
12
|
+
const win = doc.defaultView;
|
|
13
|
+
const style = doc.createElement("style");
|
|
14
|
+
style.id = STYLE_ELEMENT_ID;
|
|
15
|
+
style.textContent = STYLES;
|
|
16
|
+
const box = doc.createElement("div");
|
|
17
|
+
box.setAttribute("data-crafty-overlay", "");
|
|
18
|
+
box.className = "crafty-ve-box";
|
|
19
|
+
box.hidden = true;
|
|
20
|
+
const label = doc.createElement("span");
|
|
21
|
+
label.className = "crafty-ve-label";
|
|
22
|
+
box.appendChild(label);
|
|
23
|
+
let target = null;
|
|
24
|
+
let flashed = [];
|
|
25
|
+
let flashTimer;
|
|
26
|
+
const Observer = win?.ResizeObserver;
|
|
27
|
+
const place = () => {
|
|
28
|
+
if (target === null) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const rect = target.getBoundingClientRect();
|
|
32
|
+
box.style.top = `${rect.top}px`;
|
|
33
|
+
box.style.left = `${rect.left}px`;
|
|
34
|
+
box.style.width = `${rect.width}px`;
|
|
35
|
+
box.style.height = `${rect.height}px`;
|
|
36
|
+
};
|
|
37
|
+
const resizeObserver = typeof Observer === "function" ? new Observer(() => place()) : null;
|
|
38
|
+
const track = (element) => {
|
|
39
|
+
if (target === element) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (target !== null) {
|
|
43
|
+
resizeObserver?.unobserve(target);
|
|
44
|
+
}
|
|
45
|
+
target = element;
|
|
46
|
+
if (target !== null) {
|
|
47
|
+
resizeObserver?.observe(target);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const clearFlash = () => {
|
|
51
|
+
flashed.forEach((element) => element.classList.remove(FLASH_CLASS));
|
|
52
|
+
flashed = [];
|
|
53
|
+
if (flashTimer !== undefined) {
|
|
54
|
+
win?.clearTimeout(flashTimer);
|
|
55
|
+
flashTimer = undefined;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const ensureAttached = () => {
|
|
59
|
+
if (!style.isConnected) {
|
|
60
|
+
(doc.head ?? doc.documentElement).appendChild(style);
|
|
61
|
+
}
|
|
62
|
+
if (!box.isConnected) {
|
|
63
|
+
(doc.body ?? doc.documentElement).appendChild(box);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
ensureAttached();
|
|
67
|
+
win?.addEventListener("scroll", place, { capture: true, passive: true });
|
|
68
|
+
win?.addEventListener("resize", place, { passive: true });
|
|
69
|
+
return {
|
|
70
|
+
show(element, text) {
|
|
71
|
+
ensureAttached();
|
|
72
|
+
track(element);
|
|
73
|
+
if (label.textContent !== text) {
|
|
74
|
+
label.textContent = text;
|
|
75
|
+
}
|
|
76
|
+
box.hidden = false;
|
|
77
|
+
place();
|
|
78
|
+
},
|
|
79
|
+
hide() {
|
|
80
|
+
track(null);
|
|
81
|
+
box.hidden = true;
|
|
82
|
+
},
|
|
83
|
+
reposition() {
|
|
84
|
+
place();
|
|
85
|
+
},
|
|
86
|
+
flash(elements, durationMs) {
|
|
87
|
+
clearFlash();
|
|
88
|
+
flashed = [...elements];
|
|
89
|
+
flashed.forEach((element) => element.classList.add(FLASH_CLASS));
|
|
90
|
+
flashTimer = win?.setTimeout(clearFlash, durationMs);
|
|
91
|
+
},
|
|
92
|
+
ensureAttached,
|
|
93
|
+
contains(node) {
|
|
94
|
+
return node !== null && (box === node || box.contains(node) || style === node || style.contains(node));
|
|
95
|
+
},
|
|
96
|
+
destroy() {
|
|
97
|
+
clearFlash();
|
|
98
|
+
win?.removeEventListener("scroll", place, { capture: true });
|
|
99
|
+
win?.removeEventListener("resize", place);
|
|
100
|
+
resizeObserver?.disconnect();
|
|
101
|
+
box.remove();
|
|
102
|
+
style.remove();
|
|
103
|
+
target = null;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/visual-editing/overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
2
|
+
export type CraftyTargetType = "block" | "entry";
|
|
3
|
+
export type CraftyFieldRef = {
|
|
4
|
+
type: CraftyTargetType;
|
|
5
|
+
id: number;
|
|
6
|
+
field: string;
|
|
7
|
+
};
|
|
8
|
+
export type CraftyMarkedField = CraftyFieldRef & {
|
|
9
|
+
label?: string;
|
|
10
|
+
};
|
|
11
|
+
export type CraftyShownPage = {
|
|
12
|
+
id: number;
|
|
13
|
+
slug: string;
|
|
14
|
+
};
|
|
15
|
+
export type CraftyShownEntry = {
|
|
16
|
+
id: number;
|
|
17
|
+
collection: string;
|
|
18
|
+
};
|
|
19
|
+
export type ReadyPayload = {
|
|
20
|
+
sdkVersion: string;
|
|
21
|
+
path: string;
|
|
22
|
+
fields: CraftyMarkedField[];
|
|
23
|
+
page: CraftyShownPage | null;
|
|
24
|
+
entries: CraftyShownEntry[];
|
|
25
|
+
};
|
|
26
|
+
type Version = typeof PROTOCOL_VERSION;
|
|
27
|
+
type EmptyPayload = Record<string, never>;
|
|
28
|
+
/** iframe -> admin. `crafty:ready` is a snapshot that may be re-sent; the last one wins. */
|
|
29
|
+
export type OutboundMessage = {
|
|
30
|
+
v: Version;
|
|
31
|
+
type: "crafty:ready";
|
|
32
|
+
payload: ReadyPayload;
|
|
33
|
+
} | {
|
|
34
|
+
v: Version;
|
|
35
|
+
type: "crafty:select";
|
|
36
|
+
payload: CraftyMarkedField;
|
|
37
|
+
} | {
|
|
38
|
+
v: Version;
|
|
39
|
+
type: "crafty:navigate";
|
|
40
|
+
payload: {
|
|
41
|
+
path: string;
|
|
42
|
+
};
|
|
43
|
+
} | {
|
|
44
|
+
v: Version;
|
|
45
|
+
type: "crafty:preview-invalid";
|
|
46
|
+
payload: EmptyPayload;
|
|
47
|
+
};
|
|
48
|
+
/** admin -> iframe. */
|
|
49
|
+
export type InboundMessage = {
|
|
50
|
+
v: Version;
|
|
51
|
+
type: "crafty:token";
|
|
52
|
+
payload: {
|
|
53
|
+
token: string;
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
v: Version;
|
|
57
|
+
type: "crafty:refresh";
|
|
58
|
+
payload: EmptyPayload;
|
|
59
|
+
} | {
|
|
60
|
+
v: Version;
|
|
61
|
+
type: "crafty:highlight";
|
|
62
|
+
payload: CraftyFieldRef;
|
|
63
|
+
} | {
|
|
64
|
+
v: Version;
|
|
65
|
+
type: "crafty:preview-field";
|
|
66
|
+
payload: CraftyFieldRef & {
|
|
67
|
+
value: string;
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
v: Version;
|
|
71
|
+
type: "crafty:preview-reset";
|
|
72
|
+
payload: CraftyFieldRef;
|
|
73
|
+
};
|
|
74
|
+
export declare function parseFieldRef(value: unknown): CraftyFieldRef | null;
|
|
75
|
+
export declare function parseMarkedField(value: unknown): CraftyMarkedField | null;
|
|
76
|
+
export declare function parseInboundMessage(data: unknown): InboundMessage | null;
|
|
77
|
+
export declare function parseOutboundMessage(data: unknown): OutboundMessage | null;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/visual-editing/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAC;AACjD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B,CAAC;AAEF,KAAK,OAAO,GAAG,OAAO,gBAAgB,CAAC;AACvC,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1C,4FAA4F;AAC5F,MAAM,MAAM,eAAe,GACvB;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC3D;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GACjE;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAClE;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAE1E,uBAAuB;AACvB,MAAM,MAAM,cAAc,GACtB;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAChE;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC7D;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GACjE;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,cAAc,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACzF;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CAAC;AAqB1E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAgBnE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAWzE;AAyCD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAwCxE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAoD1E"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export const PROTOCOL_VERSION = 1;
|
|
2
|
+
/** Same limits as the Crafty admin and backend (content_drafts.field / label). */
|
|
3
|
+
const MAX_FIELD_LENGTH = 255;
|
|
4
|
+
const MAX_LABEL_LENGTH = 120;
|
|
5
|
+
const MAX_TOKEN_LENGTH = 4096;
|
|
6
|
+
const MAX_VALUE_LENGTH = 100_000;
|
|
7
|
+
const MAX_PATH_LENGTH = 2048;
|
|
8
|
+
const FIELD_PATTERN = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+|\[\d+\])*$/;
|
|
9
|
+
const TOKEN_PATTERN = /^[\x21-\x7E]+$/;
|
|
10
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001F\u007F]/;
|
|
11
|
+
const PATH_BASE = "https://crafty.invalid";
|
|
12
|
+
function isRecord(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
function isPositiveInteger(value) {
|
|
16
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
17
|
+
}
|
|
18
|
+
export function parseFieldRef(value) {
|
|
19
|
+
if (!isRecord(value)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const { type, id, field } = value;
|
|
23
|
+
if ((type !== "block" && type !== "entry") || !isPositiveInteger(id) || typeof field !== "string") {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const trimmed = field.trim();
|
|
27
|
+
return trimmed === "" || trimmed.length > MAX_FIELD_LENGTH || !FIELD_PATTERN.test(trimmed)
|
|
28
|
+
? null
|
|
29
|
+
: { type, id, field: trimmed };
|
|
30
|
+
}
|
|
31
|
+
export function parseMarkedField(value) {
|
|
32
|
+
const ref = parseFieldRef(value);
|
|
33
|
+
if (ref === null) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const rawLabel = isRecord(value) ? value.label : undefined;
|
|
37
|
+
const label = typeof rawLabel === "string" ? rawLabel.trim().slice(0, MAX_LABEL_LENGTH).trim() : "";
|
|
38
|
+
return label === "" ? ref : { ...ref, label };
|
|
39
|
+
}
|
|
40
|
+
function isSameOriginPath(value) {
|
|
41
|
+
if (!value.startsWith("/") || value.startsWith("//") || value.startsWith("/\\")) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(value, PATH_BASE);
|
|
46
|
+
// Dot segments can still resolve to a protocol-relative path ("/.//evil.example").
|
|
47
|
+
return url.origin === PATH_BASE && !url.pathname.startsWith("//") && !url.pathname.startsWith("/\\");
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function parsePath(value) {
|
|
54
|
+
if (typeof value !== "string" || value.length > MAX_PATH_LENGTH || CONTROL_CHARACTERS.test(value)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return isSameOriginPath(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
function parseShownEntry(value) {
|
|
60
|
+
return isRecord(value) && isPositiveInteger(value.id) && typeof value.collection === "string"
|
|
61
|
+
? { id: value.id, collection: value.collection }
|
|
62
|
+
: null;
|
|
63
|
+
}
|
|
64
|
+
function readEnvelope(data) {
|
|
65
|
+
if (!isRecord(data) || data.v !== PROTOCOL_VERSION || typeof data.type !== "string") {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const payload = data.payload === undefined ? {} : data.payload;
|
|
69
|
+
return isRecord(payload) ? { type: data.type, payload } : null;
|
|
70
|
+
}
|
|
71
|
+
export function parseInboundMessage(data) {
|
|
72
|
+
const message = readEnvelope(data);
|
|
73
|
+
if (message === null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const { payload } = message;
|
|
77
|
+
switch (message.type) {
|
|
78
|
+
case "crafty:token": {
|
|
79
|
+
const token = typeof payload.token === "string" ? payload.token.trim() : "";
|
|
80
|
+
return token !== "" && token.length <= MAX_TOKEN_LENGTH && TOKEN_PATTERN.test(token)
|
|
81
|
+
? { v: PROTOCOL_VERSION, type: "crafty:token", payload: { token } }
|
|
82
|
+
: null;
|
|
83
|
+
}
|
|
84
|
+
case "crafty:refresh":
|
|
85
|
+
return { v: PROTOCOL_VERSION, type: "crafty:refresh", payload: {} };
|
|
86
|
+
case "crafty:highlight": {
|
|
87
|
+
const ref = parseFieldRef(payload);
|
|
88
|
+
return ref ? { v: PROTOCOL_VERSION, type: "crafty:highlight", payload: ref } : null;
|
|
89
|
+
}
|
|
90
|
+
case "crafty:preview-field": {
|
|
91
|
+
const ref = parseFieldRef(payload);
|
|
92
|
+
const value = payload.value;
|
|
93
|
+
return ref && typeof value === "string" && value.length <= MAX_VALUE_LENGTH
|
|
94
|
+
? { v: PROTOCOL_VERSION, type: "crafty:preview-field", payload: { ...ref, value } }
|
|
95
|
+
: null;
|
|
96
|
+
}
|
|
97
|
+
case "crafty:preview-reset": {
|
|
98
|
+
const ref = parseFieldRef(payload);
|
|
99
|
+
return ref ? { v: PROTOCOL_VERSION, type: "crafty:preview-reset", payload: ref } : null;
|
|
100
|
+
}
|
|
101
|
+
default:
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function parseOutboundMessage(data) {
|
|
106
|
+
const message = readEnvelope(data);
|
|
107
|
+
if (message === null) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const { payload } = message;
|
|
111
|
+
switch (message.type) {
|
|
112
|
+
case "crafty:ready": {
|
|
113
|
+
const path = parsePath(payload.path);
|
|
114
|
+
const rawPage = payload.page;
|
|
115
|
+
if (typeof payload.sdkVersion !== "string" || path === null || !Array.isArray(payload.fields) || !Array.isArray(payload.entries)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
v: PROTOCOL_VERSION,
|
|
120
|
+
type: "crafty:ready",
|
|
121
|
+
payload: {
|
|
122
|
+
sdkVersion: payload.sdkVersion,
|
|
123
|
+
path,
|
|
124
|
+
fields: payload.fields
|
|
125
|
+
.map((field) => parseMarkedField(field))
|
|
126
|
+
.filter((field) => field !== null),
|
|
127
|
+
page: isRecord(rawPage) && isPositiveInteger(rawPage.id) && typeof rawPage.slug === "string"
|
|
128
|
+
? { id: rawPage.id, slug: rawPage.slug }
|
|
129
|
+
: null,
|
|
130
|
+
entries: payload.entries
|
|
131
|
+
.map((entry) => parseShownEntry(entry))
|
|
132
|
+
.filter((entry) => entry !== null)
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
case "crafty:select": {
|
|
137
|
+
const field = parseMarkedField(payload);
|
|
138
|
+
return field ? { v: PROTOCOL_VERSION, type: "crafty:select", payload: field } : null;
|
|
139
|
+
}
|
|
140
|
+
case "crafty:navigate": {
|
|
141
|
+
const path = parsePath(payload.path);
|
|
142
|
+
return path ? { v: PROTOCOL_VERSION, type: "crafty:navigate", payload: { path } } : null;
|
|
143
|
+
}
|
|
144
|
+
case "crafty:preview-invalid":
|
|
145
|
+
return { v: PROTOCOL_VERSION, type: "crafty:preview-invalid", payload: {} };
|
|
146
|
+
default:
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/visual-editing/protocol.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@craftware/crafty-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
".": {
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
14
|
"import": "./dist/index.js"
|
|
15
|
-
}
|
|
15
|
+
},
|
|
16
|
+
"./visual-editing": {
|
|
17
|
+
"types": "./dist/visual-editing/index.d.ts",
|
|
18
|
+
"import": "./dist/visual-editing/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
16
21
|
},
|
|
17
22
|
"main": "./dist/index.js",
|
|
18
23
|
"module": "./dist/index.js",
|
|
@@ -25,14 +30,18 @@
|
|
|
25
30
|
"node": ">=20"
|
|
26
31
|
},
|
|
27
32
|
"scripts": {
|
|
28
|
-
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"build": "node scripts/write-version.mjs && tsc -p tsconfig.json && node scripts/build-cdn.mjs",
|
|
29
34
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"test": "npm run build && vitest run",
|
|
30
36
|
"codegen": "node scripts/codegen.mjs",
|
|
31
37
|
"changeset": "npx -y @changesets/cli@2.29.7",
|
|
32
38
|
"changeset:add": "npm run changeset -- add",
|
|
33
39
|
"changeset:version": "npm run changeset -- version"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"
|
|
42
|
+
"esbuild": "^0.28.2",
|
|
43
|
+
"happy-dom": "^20.14.5",
|
|
44
|
+
"typescript": "^5.8.2",
|
|
45
|
+
"vitest": "^4.1.11"
|
|
37
46
|
}
|
|
38
47
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: crafty-cms
|
|
3
|
-
description: "Build on Crafty CMS — configure sites, pages, blocks, collections, entries, forms, and media through the Crafty MCP server, then develop a frontend with the @craftware/crafty-sdk JavaScript SDK. Use when connecting an agent to a Crafty MCP server; calling Crafty MCP tools like list_sites, create_page, publish_form, or upload_media; installing or importing @craftware/crafty-sdk or createCraftyClient; fetching Crafty pages, entries, collections, or media; rendering page blocks; or
|
|
3
|
+
description: "Build on Crafty CMS — configure sites, pages, blocks, collections, entries, forms, and media through the Crafty MCP server, then develop a frontend with the @craftware/crafty-sdk JavaScript SDK. Use when connecting an agent to a Crafty MCP server; calling Crafty MCP tools like list_sites, create_page, publish_form, or upload_media; installing or importing @craftware/crafty-sdk or createCraftyClient; fetching Crafty pages, entries, collections, or media; rendering page blocks; submitting Crafty forms; or preparing a site for the Crafty visual editor (crafty.attrs, enableVisualEditing, preview tokens, configure_visual_editor)."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: craftware
|
|
@@ -64,6 +64,7 @@ Collections/entries: `list_collections` → `create_entry` (`collection_slug`, `
|
|
|
64
64
|
Forms: `create_form` (`site_id`, `name`, `slug`, `fields`) → `publish_form` → `list_form_submissions`.
|
|
65
65
|
Media: `upload_media` (`url`, `site_id`, optional `collection_name`).
|
|
66
66
|
Cache: `set_api_cache_status` (`site_id`, `enabled`) to toggle per-site; `flush_api_cache` (`site_id`, optional `resource`) to invalidate cached responses after bulk edits.
|
|
67
|
+
Visual editor: `configure_visual_editor` (`site_id`, optional `environment`, `frontend_url`, `routes`, `render_mode`, `upload_media_collection`) — see `references/visual-editor.md`.
|
|
67
68
|
|
|
68
69
|
> `update_page` **replaces all blocks** when `blocks` is supplied. `update_entry` creates a revision. `create_page` always starts as `draft`.
|
|
69
70
|
|
|
@@ -117,6 +118,17 @@ crafty deploy # uploads dist/ to the configured environment
|
|
|
117
118
|
|
|
118
119
|
> `crafty.json` is safe to commit. `.env` (which contains your API key) must be gitignored — use `.env.example` with placeholder values.
|
|
119
120
|
|
|
121
|
+
## Visual editor (click-to-edit) — always ask
|
|
122
|
+
|
|
123
|
+
Whenever you integrate Crafty into a frontend, ask the user exactly once:
|
|
124
|
+
|
|
125
|
+
> **¿Quieres dejar el sitio preparado para el editor visual de Crafty?**
|
|
126
|
+
|
|
127
|
+
- **Yes** → follow `references/visual-editor.md` completely: mark content with `crafty.attrs()`, deliver the preview token for the site's render mode, call `enableVisualEditing()` (pass `client` for `ssr` sites, so the bridge can confirm the token itself), allow framing (`frame-ancestors`), check images are public, run `configure_visual_editor`, and verify.
|
|
128
|
+
- **No** → add nothing and tell the user it can be enabled later by asking «habilita el editor visual de Crafty».
|
|
129
|
+
|
|
130
|
+
A later request like «habilita el editor visual de Crafty» on an existing site runs the Yes flow without asking again.
|
|
131
|
+
|
|
120
132
|
## Key distinction: MCP auth vs SDK API key
|
|
121
133
|
|
|
122
134
|
These are two different credentials — do not mix them:
|
|
@@ -138,3 +150,4 @@ These are two different credentials — do not mix them:
|
|
|
138
150
|
|
|
139
151
|
- `references/mcp-configuration.md` — connecting every client, scopes, and the full MCP tool catalog with workflows.
|
|
140
152
|
- `references/sdk-development.md` — the complete SDK surface, block helpers, error handling, and examples.
|
|
153
|
+
- `references/visual-editor.md` — preparing a site for the visual editor: the mandatory question, marking, preview tokens, bridge, headers, `configure_visual_editor`, verification.
|
|
@@ -140,6 +140,7 @@ Form submissions may contain personal data, so they require their own scope.
|
|
|
140
140
|
| `upload_media` | `url`, `site_id` | `collection_name` (default `library`), `file_name` | downloads the file from the URL |
|
|
141
141
|
| `create_form` | `site_id`, `name`, `slug` | `description`, `fields` | `fields: [{ key, type, label, required? }]`; starts as `draft` |
|
|
142
142
|
| `update_form` | `form_id` | `name`, `slug`, `description`, `fields`, `notification_emails` | only supplied fields change; `fields`/`notification_emails` replace |
|
|
143
|
+
| `configure_visual_editor` | `site_id` | `environment` (default `production`), `frontend_url`, `routes` (`page_pattern`, `home_slug`, `collections`, `fixed`), `render_mode` (`ssr`/`hybrid`/`static`/`unknown`), `upload_media_collection` | visual editor settings of one environment; only sent keys change, `null` resets to the fallback; with only `site_id` it just reads; returns the effective settings |
|
|
143
144
|
|
|
144
145
|
### Publish (`crafty:publish`)
|
|
145
146
|
|
|
@@ -176,6 +177,12 @@ Form submissions may contain personal data, so they require their own scope.
|
|
|
176
177
|
1. `upload_media` with `url` and `site_id` (optionally `collection_name`).
|
|
177
178
|
2. `list_media` to confirm and retrieve URLs.
|
|
178
179
|
|
|
180
|
+
**Visual editor**
|
|
181
|
+
|
|
182
|
+
1. Prepare the frontend (see `visual-editor.md`).
|
|
183
|
+
2. `configure_visual_editor` with `site_id`, `frontend_url`, `routes`, and `render_mode`.
|
|
184
|
+
3. Ask the user to open **Editar sitio** in the admin to confirm click-to-edit works.
|
|
185
|
+
|
|
179
186
|
**API response cache management**
|
|
180
187
|
|
|
181
188
|
The public API has a tag-based response cache (enabled per environment). Per-site overrides take priority over the global setting.
|
|
@@ -23,7 +23,7 @@ const crafty = createCraftyClient({
|
|
|
23
23
|
});
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
`createCraftyClient(config)` returns a plain object with six resources
|
|
26
|
+
`createCraftyClient(config)` returns a plain object with six resources (`collections`, `entries`, `pages`, `forms`, `mediaCollections`, `mediaFiles`) and the visual editor helpers `attrs`, `attrsHtml` and `previewFromRequest`.
|
|
27
27
|
|
|
28
28
|
### Config options
|
|
29
29
|
|
|
@@ -35,6 +35,8 @@ const crafty = createCraftyClient({
|
|
|
35
35
|
| `fetch` | no | Custom fetch. Defaults to `globalThis.fetch`. If none is available, construction throws `CraftyError` (`NETWORK_ERROR`). |
|
|
36
36
|
| `headers` | no | Default headers merged into every request. |
|
|
37
37
|
| `timeoutMs` | no | Default per-request timeout. A timeout is only armed when the value is a number `> 0`. |
|
|
38
|
+
| `preview` | no | Visual editor preview token sent as `X-Crafty-Preview`. Omit in the browser (read from `?crafty_preview=`, kept in `sessionStorage`); pass `previewFromRequest(req)` on the server; `false` never sends one. |
|
|
39
|
+
| `onPreviewInvalid` | no | Called when a request that carried a token gets `X-Crafty-Preview-Status: invalid` (the response is the public content). |
|
|
38
40
|
|
|
39
41
|
## Per-call options
|
|
40
42
|
|
|
@@ -191,6 +193,16 @@ try {
|
|
|
191
193
|
}
|
|
192
194
|
```
|
|
193
195
|
|
|
196
|
+
## Visual editor helpers
|
|
197
|
+
|
|
198
|
+
- `attrs(target, field, { label? })` / `crafty.attrs(...)` → `{ "data-crafty-block" | "data-crafty-entry", "data-crafty-field", "data-crafty-label"? }` to spread on the element that renders the value; `{}` when the target has no valid id. `field` is a field key or a JSON path (`sections[3].title`).
|
|
199
|
+
- `attrsHtml(target, field, { label? })` → the same attributes as an escaped HTML string.
|
|
200
|
+
- `previewFromRequest(req)` → preview token of an incoming request (URL string, `URL`, Web `Request`, Node `req.url`, Express `req.query`) or `null`.
|
|
201
|
+
- `readPreviewToken()` → token in the browser, or `null`.
|
|
202
|
+
- `enableVisualEditing(options)` from `@craftware/crafty-sdk/visual-editing` → connects the page to the editor; no-op outside the admin iframe. Options: `onRefresh`, `adminOrigins`, `page`, `entries`, `touchHighlightMs`.
|
|
203
|
+
|
|
204
|
+
Full integration checklist: `visual-editor.md`.
|
|
205
|
+
|
|
194
206
|
## Notes
|
|
195
207
|
|
|
196
208
|
- `Accept: application/json` is set automatically unless overridden.
|