@cmssy/react 12.3.0 → 12.4.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/client.cjs CHANGED
@@ -38,6 +38,34 @@ function blocksToMeta(blocks, defaults = {}) {
38
38
  }
39
39
  return out;
40
40
  }
41
+
42
+ // src/bridge/shortcut-keys.ts
43
+ function isMacPlatform() {
44
+ const platform = typeof navigator !== "undefined" ? navigator.platform : void 0;
45
+ return typeof platform === "string" && platform.toUpperCase().includes("MAC");
46
+ }
47
+ function isTypingTarget(target) {
48
+ const el = target;
49
+ if (!el || typeof el.tagName !== "string") return false;
50
+ return el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable === true;
51
+ }
52
+ function resolveShortcutAction(event, isMac, isTyping) {
53
+ if (event.repeat || event.isComposing) return null;
54
+ const modifier = isMac ? event.metaKey : event.ctrlKey;
55
+ const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
56
+ if (modifier && key === "z") {
57
+ if (isTyping) return null;
58
+ return event.shiftKey ? "redo" : "undo";
59
+ }
60
+ if (modifier && key === "s") return "save";
61
+ if (modifier && key === "d") return isTyping ? null : "duplicate";
62
+ const isDeleteKey = key === "Delete" || isMac && key === "Backspace";
63
+ if (isDeleteKey) return isTyping ? null : "delete";
64
+ if (key === "Escape") return "escape";
65
+ return null;
66
+ }
67
+
68
+ // src/bridge/use-edit-bridge.tsx
41
69
  var ZERO_RECT = { x: 0, y: 0, width: 0, height: 0 };
42
70
  function collectRects() {
43
71
  const rects = /* @__PURE__ */ new Map();
@@ -143,7 +171,8 @@ function useEditBridge(page, config) {
143
171
  ...collectLayoutBlocks(rects, pageIds)
144
172
  ],
145
173
  schemas: config.schemas ?? /* @__PURE__ */ Object.create(null),
146
- blockMeta: config.blockMeta ?? /* @__PURE__ */ Object.create(null)
174
+ blockMeta: config.blockMeta ?? /* @__PURE__ */ Object.create(null),
175
+ capabilities: ["shortcuts"]
147
176
  });
148
177
  } catch (error) {
149
178
  if (typeof console !== "undefined") {
@@ -238,6 +267,20 @@ function useEditBridge(page, config) {
238
267
  ...layoutPosition !== null ? { layoutPosition } : {}
239
268
  });
240
269
  };
270
+ const onKeyDown = (event) => {
271
+ const action = resolveShortcutAction(
272
+ event,
273
+ isMacPlatform(),
274
+ isTypingTarget(event.target)
275
+ );
276
+ if (!action) return;
277
+ event.preventDefault();
278
+ postSafe({
279
+ type: "cmssy:shortcut",
280
+ protocolVersion: core.PROTOCOL_VERSION,
281
+ action
282
+ });
283
+ };
241
284
  let boundsRaf = 0;
242
285
  let boundsPending = false;
243
286
  const emitSelectedBounds = () => {
@@ -261,6 +304,7 @@ function useEditBridge(page, config) {
261
304
  emitBoundsRef.current = emitSelectedBounds;
262
305
  window.addEventListener("message", handler);
263
306
  document.addEventListener("click", onClick, { capture: true });
307
+ document.addEventListener("keydown", onKeyDown, { capture: true });
264
308
  window.addEventListener("scroll", emitSelectedBounds, {
265
309
  capture: true,
266
310
  passive: true
@@ -271,6 +315,7 @@ function useEditBridge(page, config) {
271
315
  if (boundsRaf) cancelAnimationFrame(boundsRaf);
272
316
  window.removeEventListener("message", handler);
273
317
  document.removeEventListener("click", onClick, { capture: true });
318
+ document.removeEventListener("keydown", onKeyDown, { capture: true });
274
319
  window.removeEventListener("scroll", emitSelectedBounds, {
275
320
  capture: true
276
321
  });
package/dist/client.js CHANGED
@@ -36,6 +36,34 @@ function blocksToMeta(blocks, defaults = {}) {
36
36
  }
37
37
  return out;
38
38
  }
39
+
40
+ // src/bridge/shortcut-keys.ts
41
+ function isMacPlatform() {
42
+ const platform = typeof navigator !== "undefined" ? navigator.platform : void 0;
43
+ return typeof platform === "string" && platform.toUpperCase().includes("MAC");
44
+ }
45
+ function isTypingTarget(target) {
46
+ const el = target;
47
+ if (!el || typeof el.tagName !== "string") return false;
48
+ return el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable === true;
49
+ }
50
+ function resolveShortcutAction(event, isMac, isTyping) {
51
+ if (event.repeat || event.isComposing) return null;
52
+ const modifier = isMac ? event.metaKey : event.ctrlKey;
53
+ const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
54
+ if (modifier && key === "z") {
55
+ if (isTyping) return null;
56
+ return event.shiftKey ? "redo" : "undo";
57
+ }
58
+ if (modifier && key === "s") return "save";
59
+ if (modifier && key === "d") return isTyping ? null : "duplicate";
60
+ const isDeleteKey = key === "Delete" || isMac && key === "Backspace";
61
+ if (isDeleteKey) return isTyping ? null : "delete";
62
+ if (key === "Escape") return "escape";
63
+ return null;
64
+ }
65
+
66
+ // src/bridge/use-edit-bridge.tsx
39
67
  var ZERO_RECT = { x: 0, y: 0, width: 0, height: 0 };
40
68
  function collectRects() {
41
69
  const rects = /* @__PURE__ */ new Map();
@@ -141,7 +169,8 @@ function useEditBridge(page, config) {
141
169
  ...collectLayoutBlocks(rects, pageIds)
142
170
  ],
143
171
  schemas: config.schemas ?? /* @__PURE__ */ Object.create(null),
144
- blockMeta: config.blockMeta ?? /* @__PURE__ */ Object.create(null)
172
+ blockMeta: config.blockMeta ?? /* @__PURE__ */ Object.create(null),
173
+ capabilities: ["shortcuts"]
145
174
  });
146
175
  } catch (error) {
147
176
  if (typeof console !== "undefined") {
@@ -236,6 +265,20 @@ function useEditBridge(page, config) {
236
265
  ...layoutPosition !== null ? { layoutPosition } : {}
237
266
  });
238
267
  };
268
+ const onKeyDown = (event) => {
269
+ const action = resolveShortcutAction(
270
+ event,
271
+ isMacPlatform(),
272
+ isTypingTarget(event.target)
273
+ );
274
+ if (!action) return;
275
+ event.preventDefault();
276
+ postSafe({
277
+ type: "cmssy:shortcut",
278
+ protocolVersion: PROTOCOL_VERSION,
279
+ action
280
+ });
281
+ };
239
282
  let boundsRaf = 0;
240
283
  let boundsPending = false;
241
284
  const emitSelectedBounds = () => {
@@ -259,6 +302,7 @@ function useEditBridge(page, config) {
259
302
  emitBoundsRef.current = emitSelectedBounds;
260
303
  window.addEventListener("message", handler);
261
304
  document.addEventListener("click", onClick, { capture: true });
305
+ document.addEventListener("keydown", onKeyDown, { capture: true });
262
306
  window.addEventListener("scroll", emitSelectedBounds, {
263
307
  capture: true,
264
308
  passive: true
@@ -269,6 +313,7 @@ function useEditBridge(page, config) {
269
313
  if (boundsRaf) cancelAnimationFrame(boundsRaf);
270
314
  window.removeEventListener("message", handler);
271
315
  document.removeEventListener("click", onClick, { capture: true });
316
+ document.removeEventListener("keydown", onKeyDown, { capture: true });
272
317
  window.removeEventListener("scroll", emitSelectedBounds, {
273
318
  capture: true
274
319
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/react",
3
- "version": "12.3.0",
3
+ "version": "12.4.0",
4
4
  "description": "React blocks, renderers, data client and editor bridge for cmssy headless sites",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -97,7 +97,7 @@
97
97
  },
98
98
  "dependencies": {
99
99
  "@cmssy/types": "0.35.0",
100
- "@cmssy/core": "12.3.0"
100
+ "@cmssy/core": "12.4.0"
101
101
  },
102
102
  "scripts": {
103
103
  "build": "tsup",