@dfosco/storyboard-react 4.0.0-beta.6 → 4.0.0-beta.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/package.json +3 -3
- package/src/canvas/CanvasPage.jsx +58 -1
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard-react",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@dfosco/storyboard-core": "4.0.0-beta.
|
|
7
|
-
"@dfosco/tiny-canvas": "4.0.0-beta.
|
|
6
|
+
"@dfosco/storyboard-core": "4.0.0-beta.8",
|
|
7
|
+
"@dfosco/tiny-canvas": "4.0.0-beta.8",
|
|
8
8
|
"@neodrag/react": "^2.3.1",
|
|
9
9
|
"glob": "^11.0.0",
|
|
10
10
|
"jsonc-parser": "^3.3.1",
|
|
@@ -47,6 +47,35 @@ function resolveCanvasThemeFromStorage() {
|
|
|
47
47
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Get the copyable URL for a widget based on its type.
|
|
52
|
+
* Returns the most relevant URL/path for the widget content.
|
|
53
|
+
*/
|
|
54
|
+
function getWidgetCopyableUrl(widget) {
|
|
55
|
+
const { type, props = {} } = widget
|
|
56
|
+
const base = (typeof import.meta !== 'undefined' && import.meta.env?.BASE_URL) || '/'
|
|
57
|
+
switch (type) {
|
|
58
|
+
case 'prototype':
|
|
59
|
+
// Prototype src is a path like "/MyPrototype" - make it a full URL
|
|
60
|
+
return props.src ? `${window.location.origin}${base.replace(/\/$/, '')}${props.src}` : ''
|
|
61
|
+
case 'figma-embed':
|
|
62
|
+
return props.url || ''
|
|
63
|
+
case 'link-preview':
|
|
64
|
+
return props.url || ''
|
|
65
|
+
case 'image':
|
|
66
|
+
// Return the served image URL
|
|
67
|
+
return props.src ? `${window.location.origin}${base.replace(/\/$/, '')}/_storyboard/canvas/images/${props.src}` : ''
|
|
68
|
+
case 'sticky-note':
|
|
69
|
+
// Sticky notes have text content, not a URL
|
|
70
|
+
return props.text || ''
|
|
71
|
+
case 'markdown':
|
|
72
|
+
// Markdown has content, not a URL
|
|
73
|
+
return props.content || ''
|
|
74
|
+
default:
|
|
75
|
+
return ''
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
50
79
|
/**
|
|
51
80
|
* Debounce helper — returns a function that delays invocation.
|
|
52
81
|
* Exposes `.cancel()` to abort pending calls (used by undo/redo).
|
|
@@ -977,6 +1006,34 @@ export default function CanvasPage({ name }) {
|
|
|
977
1006
|
e.preventDefault()
|
|
978
1007
|
setSelectedWidgetIds(new Set())
|
|
979
1008
|
}
|
|
1009
|
+
// Copy shortcuts (single widget selected):
|
|
1010
|
+
// - cmd+c → copy URL/content
|
|
1011
|
+
// - Shift+C (no cmd) → copy widget ID (or file path for images)
|
|
1012
|
+
const mod = e.metaKey || e.ctrlKey
|
|
1013
|
+
if (mod && e.key === 'c' && !e.shiftKey && selectedWidgetIds.size === 1) {
|
|
1014
|
+
const widgetId = [...selectedWidgetIds][0]
|
|
1015
|
+
const widget = localWidgets?.find(w => w.id === widgetId)
|
|
1016
|
+
if (widget) {
|
|
1017
|
+
e.preventDefault()
|
|
1018
|
+
const url = getWidgetCopyableUrl(widget)
|
|
1019
|
+
if (url) {
|
|
1020
|
+
navigator.clipboard.writeText(url).catch(() => {})
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// Shift+C (uppercase C, no cmd) → copy ID or file path
|
|
1025
|
+
if (e.key === 'C' && e.shiftKey && !mod && selectedWidgetIds.size === 1) {
|
|
1026
|
+
const widgetId = [...selectedWidgetIds][0]
|
|
1027
|
+
const widget = localWidgets?.find(w => w.id === widgetId)
|
|
1028
|
+
if (widget) {
|
|
1029
|
+
e.preventDefault()
|
|
1030
|
+
if (widget.type === 'image' && widget.props?.src) {
|
|
1031
|
+
navigator.clipboard.writeText(`src/canvas/images/${widget.props.src}`).catch(() => {})
|
|
1032
|
+
} else {
|
|
1033
|
+
navigator.clipboard.writeText(widgetId).catch(() => {})
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
980
1037
|
if (e.key === 'Delete' || e.key === 'Backspace') {
|
|
981
1038
|
e.preventDefault()
|
|
982
1039
|
if (selectedWidgetIds.size > 1) {
|
|
@@ -1002,7 +1059,7 @@ export default function CanvasPage({ name }) {
|
|
|
1002
1059
|
}
|
|
1003
1060
|
document.addEventListener('keydown', handleKeyDown)
|
|
1004
1061
|
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
1005
|
-
}, [selectedWidgetIds, handleWidgetRemove, undoRedo, name, debouncedSave])
|
|
1062
|
+
}, [selectedWidgetIds, localWidgets, handleWidgetRemove, undoRedo, name, debouncedSave])
|
|
1006
1063
|
|
|
1007
1064
|
// Paste handler — images become image widgets, same-origin URLs become prototypes,
|
|
1008
1065
|
// other URLs become link previews, text becomes markdown
|