@dfosco/storyboard-react 3.0.0 → 3.1.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.
@@ -4,50 +4,22 @@
4
4
 
5
5
  .sticky {
6
6
  background: var(--sticky-bg, #fff8c5);
7
- border-radius: 2px;
7
+ border-radius: 6px;
8
+ border: 2px solid color-mix(in srgb, var(--sticky-bg) 80%, rgb(0, 0, 0) 10%);
8
9
  min-width: 180px;
9
- box-shadow: 2px 3px 8px rgba(0, 0, 0, 0.08);
10
+ box-shadow: 2px 3px 8px rgba(0, 0, 0, 0.04);
10
11
  font-family: var(--tc-font-stack, system-ui, -apple-system, sans-serif);
11
12
  resize: both;
12
13
  overflow: auto;
13
14
  position: relative;
14
15
  }
15
16
 
16
- .removeBtn {
17
- all: unset;
18
- cursor: pointer;
19
- position: absolute;
20
- top: 4px;
21
- right: 4px;
22
- font-size: 16px;
23
- line-height: 1;
24
- width: 20px;
25
- height: 20px;
26
- display: flex;
27
- align-items: center;
28
- justify-content: center;
29
- color: var(--sticky-border, #d4a72c);
30
- border-radius: 2px;
31
- opacity: 0;
32
- transition: opacity 150ms;
33
- z-index: 1;
34
- }
35
-
36
- .sticky:hover .removeBtn {
37
- opacity: 0.6;
38
- }
39
-
40
- .removeBtn:hover {
41
- opacity: 1;
42
- color: var(--fgColor-danger, #d1242f);
43
- }
44
-
45
17
  .text {
46
- padding: 12px 14px;
18
+ padding: 16px 20px;
47
19
  margin: 0;
48
- font-size: 14px;
20
+ font-size: 18px;
49
21
  line-height: 1.5;
50
- color: #1a1a1a;
22
+ color: color-mix(in srgb, var(--sticky-bg), black 70%);
51
23
  white-space: pre-wrap;
52
24
  word-break: break-word;
53
25
  cursor: text;
@@ -55,21 +27,24 @@
55
27
  }
56
28
 
57
29
  .textarea {
58
- display: block;
30
+ position: absolute;
31
+ top: 0;
32
+ left: 0;
59
33
  width: 100%;
60
34
  height: 100%;
61
35
  box-sizing: border-box;
62
- padding: 12px 14px;
36
+ padding: 16px 20px;
63
37
  margin: 0;
64
38
  border: none;
65
39
  outline: none;
66
40
  background: transparent;
67
41
  font-family: inherit;
68
- font-size: 14px;
42
+ font-size: 18px;
69
43
  line-height: 1.5;
70
- color: #1a1a1a;
44
+ color: color-mix(in srgb, var(--sticky-bg) 80%, rgb(0, 0, 0) 98%);
45
+ white-space: pre-wrap;
46
+ word-break: break-word;
71
47
  resize: none;
72
- min-height: 80px;
73
48
  }
74
49
 
75
50
  /* Color picker area — sits below the sticky */
@@ -2,24 +2,11 @@ import styles from './WidgetWrapper.module.css'
2
2
 
3
3
  /**
4
4
  * Common wrapper for all canvas widgets.
5
- * Provides shadow/border styling and a remove button on hover.
5
+ * Provides shadow/border styling.
6
6
  */
7
- export default function WidgetWrapper({ children, onRemove, className }) {
7
+ export default function WidgetWrapper({ children, className }) {
8
8
  return (
9
9
  <section className={`${styles.wrapper} ${className || ''}`}>
10
- {onRemove && (
11
- <button
12
- className={styles.removeBtn}
13
- onClick={(e) => {
14
- e.stopPropagation()
15
- onRemove()
16
- }}
17
- title="Remove widget"
18
- aria-label="Remove widget"
19
- >
20
- ×
21
- </button>
22
- )}
23
10
  <div className={styles.content}>
24
11
  {children}
25
12
  </div>
@@ -9,36 +9,6 @@
9
9
  0 2px 8px rgba(0, 0, 0, 0.08);
10
10
  }
11
11
 
12
- .removeBtn {
13
- all: unset;
14
- cursor: pointer;
15
- position: absolute;
16
- top: 6px;
17
- right: 6px;
18
- font-size: 16px;
19
- line-height: 1;
20
- width: 22px;
21
- height: 22px;
22
- display: flex;
23
- align-items: center;
24
- justify-content: center;
25
- color: var(--fgColor-muted, #656d76);
26
- border-radius: 6px;
27
- background: var(--bgColor-default, #ffffff);
28
- opacity: 0;
29
- transition: opacity 150ms;
30
- z-index: 1;
31
- }
32
-
33
- .wrapper:hover .removeBtn {
34
- opacity: 1;
35
- }
36
-
37
- .removeBtn:hover {
38
- color: var(--fgColor-danger, #d1242f);
39
- background: var(--bgColor-danger-muted, #ffebe9);
40
- }
41
-
42
12
  .content {
43
13
  position: relative;
44
14
  }
@@ -1,15 +1,17 @@
1
1
  import StickyNote from './StickyNote.jsx'
2
2
  import MarkdownBlock from './MarkdownBlock.jsx'
3
3
  import PrototypeEmbed from './PrototypeEmbed.jsx'
4
+ import LinkPreview from './LinkPreview.jsx'
4
5
 
5
6
  /**
6
7
  * Maps widget type strings to their React components.
7
- * Each component receives: { id, props, onUpdate, onRemove }
8
+ * Each component receives: { id, props, onUpdate }
8
9
  */
9
10
  export const widgetRegistry = {
10
11
  'sticky-note': StickyNote,
11
12
  'markdown': MarkdownBlock,
12
13
  'prototype': PrototypeEmbed,
14
+ 'link-preview': LinkPreview,
13
15
  }
14
16
 
15
17
  /**
@@ -130,10 +130,16 @@ export const markdownSchema = {
130
130
  export const prototypeEmbedSchema = {
131
131
  src: { type: 'url', label: 'URL', category: 'content', defaultValue: '' },
132
132
  label: { type: 'text', label: 'Label', category: 'settings', defaultValue: '' },
133
+ zoom: { type: 'number', label: 'Zoom', category: 'settings', defaultValue: 100, min: 25, max: 200 },
133
134
  width: { type: 'number', label: 'Width', category: 'size', defaultValue: 800, min: 200, max: 2000 },
134
135
  height: { type: 'number', label: 'Height', category: 'size', defaultValue: 600, min: 200, max: 1500 },
135
136
  }
136
137
 
138
+ export const linkPreviewSchema = {
139
+ url: { type: 'url', label: 'URL', category: 'content', defaultValue: '' },
140
+ title: { type: 'text', label: 'Title', category: 'content', defaultValue: '' },
141
+ }
142
+
137
143
  /**
138
144
  * Schema registry — maps widget type strings to their schemas.
139
145
  */
@@ -141,4 +147,5 @@ export const schemas = {
141
147
  'sticky-note': stickyNoteSchema,
142
148
  'markdown': markdownSchema,
143
149
  'prototype': prototypeEmbedSchema,
150
+ 'link-preview': linkPreviewSchema,
144
151
  }
@@ -3,7 +3,7 @@ import path from 'node:path'
3
3
  import { execSync } from 'node:child_process'
4
4
  import { globSync } from 'glob'
5
5
  import { parse as parseJsonc } from 'jsonc-parser'
6
- import { materializeFromText } from '../../../core/src/canvas/materializer.js'
6
+ import { materializeFromText } from '@dfosco/storyboard-core/canvas/materializer'
7
7
 
8
8
  const VIRTUAL_MODULE_ID = 'virtual:storyboard-data-index'
9
9
  const RESOLVED_ID = '\0' + VIRTUAL_MODULE_ID
@@ -34,26 +34,28 @@ function parseDataFile(filePath) {
34
34
 
35
35
  const name = canvasJsonlMatch[1]
36
36
  let inferredRoute = null
37
- const canvasFolderMatch = normalized.match(/(?:^|\/)src\/canvases\/([^/]+)\.folder\//)
37
+ const canvasFolderMatch = normalized.match(/(?:^|\/)src\/canvas\/([^/]+)\.folder\//)
38
38
  const canvasFolderName = canvasFolderMatch ? canvasFolderMatch[1] : null
39
39
  const folderDirMatch = normalized.match(/(?:^|\/)src\/prototypes\/([^/]+)\.folder\//)
40
40
  const folderName = folderDirMatch ? folderDirMatch[1] : null
41
41
 
42
- const canvasCheck = normalized.match(/(?:^|\/)src\/canvases\//)
42
+ const canvasCheck = normalized.match(/(?:^|\/)src\/canvas\//)
43
43
  if (canvasCheck) {
44
44
  const dirPath = normalized.substring(0, normalized.lastIndexOf('/'))
45
- const routeBase = dirPath
46
- .replace(/^.*?src\/canvases\//, '')
45
+ const routeBase = (dirPath + '/')
46
+ .replace(/^.*?src\/canvas\//, '')
47
47
  .replace(/[^/]*\.folder\/?/g, '')
48
+ .replace(/\/$/, '')
48
49
  inferredRoute = '/canvas/' + (routeBase ? routeBase + '/' : '') + name
49
50
  inferredRoute = inferredRoute.replace(/\/+/g, '/').replace(/\/$/, '') || '/canvas'
50
51
  }
51
52
  const protoCheck = normalized.match(/(?:^|\/)src\/prototypes\//)
52
53
  if (!canvasCheck && protoCheck) {
53
54
  const dirPath = normalized.substring(0, normalized.lastIndexOf('/'))
54
- const routeBase = dirPath
55
+ const routeBase = (dirPath + '/')
55
56
  .replace(/^.*?src\/prototypes\//, '')
56
57
  .replace(/[^/]*\.folder\/?/g, '')
58
+ .replace(/\/$/, '')
57
59
  inferredRoute = '/canvas/' + (routeBase ? routeBase + '/' : '') + name
58
60
  inferredRoute = inferredRoute.replace(/\/+/g, '/').replace(/\/$/, '') || '/canvas'
59
61
  }
@@ -386,7 +388,7 @@ function generateModule({ index, protoFolders, flowRoutes, canvasRoutes }, root)
386
388
  parsed = { ...parsed, _route: canvasRoutes[name] }
387
389
  }
388
390
  // Inject folder association
389
- const folderDirMatch = path.relative(root, absPath).replace(/\\/g, '/').match(/(?:^|\/)src\/(?:prototypes|canvases)\/([^/]+)\.folder\//)
391
+ const folderDirMatch = path.relative(root, absPath).replace(/\\/g, '/').match(/(?:^|\/)src\/(?:prototypes|canvas)\/([^/]+)\.folder\//)
390
392
  if (folderDirMatch) {
391
393
  parsed = { ...parsed, _folder: folderDirMatch[1] }
392
394
  }