@dfosco/storyboard-react 4.2.0-beta.0 → 4.2.0-beta.17

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.
Files changed (48) hide show
  1. package/package.json +5 -4
  2. package/src/AuthModal/AuthModal.jsx +6 -2
  3. package/src/BranchBar/BranchBar.jsx +17 -5
  4. package/src/BranchBar/BranchBar.module.css +11 -2
  5. package/src/CommandPalette/CommandPalette.jsx +267 -164
  6. package/src/CommandPalette/command-palette.css +130 -78
  7. package/src/Icon.jsx +112 -48
  8. package/src/Viewfinder.jsx +511 -61
  9. package/src/Viewfinder.module.css +414 -2
  10. package/src/canvas/CanvasPage.bridge.test.jsx +14 -6
  11. package/src/canvas/CanvasPage.dragdrop.test.jsx +10 -6
  12. package/src/canvas/CanvasPage.jsx +157 -174
  13. package/src/canvas/CanvasPage.module.css +0 -15
  14. package/src/canvas/CanvasPage.multiselect.test.jsx +10 -6
  15. package/src/canvas/ConnectorLayer.jsx +5 -5
  16. package/src/canvas/PageSelector.test.jsx +15 -6
  17. package/src/canvas/useCanvas.js +1 -1
  18. package/src/canvas/widgets/ActionWidget.jsx +200 -0
  19. package/src/canvas/widgets/ActionWidget.module.css +122 -0
  20. package/src/canvas/widgets/FigmaEmbed.jsx +97 -29
  21. package/src/canvas/widgets/FigmaEmbed.module.css +61 -0
  22. package/src/canvas/widgets/ImageWidget.jsx +1 -1
  23. package/src/canvas/widgets/LinkPreview.jsx +64 -5
  24. package/src/canvas/widgets/LinkPreview.module.css +127 -0
  25. package/src/canvas/widgets/MarkdownBlock.jsx +39 -17
  26. package/src/canvas/widgets/MarkdownBlock.module.css +123 -0
  27. package/src/canvas/widgets/PrototypeEmbed.jsx +183 -20
  28. package/src/canvas/widgets/PrototypeEmbed.module.css +117 -0
  29. package/src/canvas/widgets/PrototypeEmbed.test.jsx +2 -2
  30. package/src/canvas/widgets/SplitExpandModal.jsx +234 -0
  31. package/src/canvas/widgets/SplitExpandModal.module.css +335 -0
  32. package/src/canvas/widgets/SplitScreenTopBar.jsx +30 -0
  33. package/src/canvas/widgets/SplitScreenTopBar.module.css +58 -0
  34. package/src/canvas/widgets/StoryWidget.jsx +7 -4
  35. package/src/canvas/widgets/TerminalReadWidget.jsx +140 -0
  36. package/src/canvas/widgets/TerminalReadWidget.module.css +92 -0
  37. package/src/canvas/widgets/TerminalWidget.jsx +299 -49
  38. package/src/canvas/widgets/TerminalWidget.module.css +155 -1
  39. package/src/canvas/widgets/WidgetChrome.jsx +19 -14
  40. package/src/canvas/widgets/WidgetChrome.module.css +10 -0
  41. package/src/canvas/widgets/embedInteraction.test.jsx +24 -26
  42. package/src/canvas/widgets/expandUtils.js +188 -0
  43. package/src/canvas/widgets/index.js +5 -0
  44. package/src/canvas/widgets/snapshotDisplay.test.jsx +23 -71
  45. package/src/canvas/widgets/widgetConfig.js +19 -1
  46. package/src/hooks/useConfig.js +14 -0
  47. package/src/index.js +4 -0
  48. package/src/vite/data-plugin.js +264 -14
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
1
+ import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from 'react'
2
2
  import { remark } from 'remark'
3
3
  import remarkGfm from 'remark-gfm'
4
4
  import remarkHtml from 'remark-html'
@@ -6,6 +6,7 @@ import { MarkGithubIcon } from '@primer/octicons-react'
6
6
  import WidgetWrapper from './WidgetWrapper.jsx'
7
7
  import ResizeHandle from './ResizeHandle.jsx'
8
8
  import { readProp, linkPreviewSchema } from './widgetProps.js'
9
+ import SplitExpandModal from './SplitExpandModal.jsx'
9
10
  import styles from './LinkPreview.module.css'
10
11
 
11
12
  const VIDEO_EXT_RE = /\.(mp4|mov|webm|ogg)(\?[^)]*)?$/i
@@ -106,7 +107,7 @@ function getCommentKindLabel(github) {
106
107
  return 'Comment'
107
108
  }
108
109
 
109
- function GitHubIssueCard({ url, title, github, width, collapsed, onUpdate }) {
110
+ function GitHubIssueCard({ id, url, title, github, width, collapsed, expanded, onCloseExpand }) {
110
111
  const authors = Array.isArray(github?.authors)
111
112
  ? github.authors.filter((a) => typeof a === 'string' && a.trim())
112
113
  : []
@@ -146,6 +147,7 @@ function GitHubIssueCard({ url, title, github, width, collapsed, onUpdate }) {
146
147
  }
147
148
 
148
149
  return (
150
+ <>
149
151
  <WidgetWrapper>
150
152
  <div className={`${styles.issueCard} ${collapsed ? styles.issueCardCollapsed : ''}`} style={sizeStyle}>
151
153
  <div className={styles.typeBar}>
@@ -204,10 +206,38 @@ function GitHubIssueCard({ url, title, github, width, collapsed, onUpdate }) {
204
206
  )}
205
207
  </div>
206
208
  </WidgetWrapper>
209
+ <SplitExpandModal
210
+ expanded={expanded}
211
+ onClose={onCloseExpand}
212
+ widgetId={id}
213
+ title={`${kindLabel}: ${titleText || url || 'GitHub'}`}
214
+ >
215
+ <div className={styles.expandedIssue}>
216
+ <header className={styles.expandedIssueHeader}>
217
+ <h2 className={styles.expandedIssueTitle}>
218
+ <a href={url || '#'} target="_blank" rel="noopener noreferrer">
219
+ {titleText || url}
220
+ {issueNumber && <span className={styles.expandedIssueNumber}> {issueNumber}</span>}
221
+ </a>
222
+ </h2>
223
+ <div className={styles.expandedByline}>
224
+ {primaryAuthor && (
225
+ <a href={`https://github.com/${primaryAuthor}`} target="_blank" rel="noopener noreferrer" className={styles.expandedAuthor}>
226
+ <img src={`https://github.com/${primaryAuthor}.png?size=40`} alt="" width="20" height="20" className={styles.avatar} loading="lazy" />
227
+ {primaryAuthor}
228
+ </a>
229
+ )}
230
+ {createdAgo && <span className={styles.expandedBylineText}>{primaryAuthor ? ` opened ${createdAgo}` : `Opened ${createdAgo}`}</span>}
231
+ </div>
232
+ </header>
233
+ {bodyHtml && <div className={styles.expandedIssueBody} dangerouslySetInnerHTML={{ __html: bodyHtml }} />}
234
+ </div>
235
+ </SplitExpandModal>
236
+ </>
207
237
  )
208
238
  }
209
239
 
210
- export default function LinkPreview({ props, onUpdate, resizable }) {
240
+ export default forwardRef(function LinkPreview({ id, props, onUpdate, resizable }, ref) {
211
241
  const url = readProp(props, 'url', linkPreviewSchema)
212
242
  const title = readProp(props, 'title', linkPreviewSchema)
213
243
  const github = props?.github && typeof props.github === 'object' ? props.github : null
@@ -222,6 +252,18 @@ export default function LinkPreview({ props, onUpdate, resizable }) {
222
252
  const cardRef = useRef(null)
223
253
  const inputRef = useRef(null)
224
254
  const [editing, setEditing] = useState(false)
255
+ const [expanded, setExpanded] = useState(false)
256
+
257
+ useImperativeHandle(ref, () => ({
258
+ handleAction(actionId) {
259
+ if (actionId === 'expand' || actionId === 'split-screen') { setExpanded(true); return true }
260
+ if (actionId === 'open-external') {
261
+ if (url) window.open(url, '_blank', 'noopener')
262
+ return true
263
+ }
264
+ return false
265
+ },
266
+ }), [url])
225
267
 
226
268
  const startEditing = useCallback(() => {
227
269
  if (!canEdit) return
@@ -242,12 +284,14 @@ export default function LinkPreview({ props, onUpdate, resizable }) {
242
284
  if (github) {
243
285
  return (
244
286
  <GitHubIssueCard
287
+ id={id}
245
288
  url={url}
246
289
  title={title}
247
290
  github={github}
248
291
  width={width}
249
292
  collapsed={!!props?.collapsed}
250
- onUpdate={onUpdate}
293
+ expanded={expanded}
294
+ onCloseExpand={() => setExpanded(false)}
251
295
  />
252
296
  )
253
297
  }
@@ -262,6 +306,7 @@ export default function LinkPreview({ props, onUpdate, resizable }) {
262
306
  const handleResize = (w, h) => onUpdate?.({ width: w, height: h })
263
307
 
264
308
  return (
309
+ <>
265
310
  <div className={styles.container}>
266
311
  <div ref={cardRef} className={styles.card} style={sizeStyle}>
267
312
  {ogImage && (
@@ -316,5 +361,19 @@ export default function LinkPreview({ props, onUpdate, resizable }) {
316
361
  </div>
317
362
  {resizable && <ResizeHandle targetRef={cardRef} width={width} height={height} onResize={handleResize} />}
318
363
  </div>
364
+ <SplitExpandModal
365
+ expanded={expanded}
366
+ onClose={() => setExpanded(false)}
367
+ widgetId={id}
368
+ title={title || hostname || 'Link Preview'}
369
+ >
370
+ <div className={styles.expandedLink}>
371
+ {ogImage && <img className={styles.expandedOgImage} src={ogImage} alt="" loading="lazy" />}
372
+ <h2 className={styles.expandedTitle}>{title || hostname || url || 'Untitled'}</h2>
373
+ {description && <p className={styles.expandedDescription}>{description}</p>}
374
+ {url && <a href={url} target="_blank" rel="noopener noreferrer" className={styles.expandedUrl}>{url}</a>}
375
+ </div>
376
+ </SplitExpandModal>
377
+ </>
319
378
  )
320
- }
379
+ })
@@ -417,3 +417,130 @@
417
417
  font-size: 12px;
418
418
  color: var(--fgColor-danger, #cf222e);
419
419
  }
420
+
421
+ /* ── Expanded issue view in modal ─────────────────────────────────── */
422
+
423
+ .expandedIssue {
424
+ height: 100%;
425
+ display: flex;
426
+ flex-direction: column;
427
+ background: var(--bgColor-default, #ffffff);
428
+ font-family: var(--tc-font-stack, system-ui, -apple-system, sans-serif);
429
+ }
430
+
431
+ .expandedIssueHeader {
432
+ padding: 24px 40px 16px;
433
+ border-bottom: 1px solid var(--borderColor-muted, #d8dee4);
434
+ }
435
+
436
+ .expandedIssueTitle {
437
+ margin: 0 0 8px;
438
+ font-size: 28px;
439
+ font-weight: 400;
440
+ line-height: 1.25;
441
+ color: var(--fgColor-default, #1f2328);
442
+ }
443
+
444
+ .expandedIssueTitle a {
445
+ color: inherit;
446
+ text-decoration: none;
447
+ }
448
+
449
+ .expandedIssueTitle a:hover {
450
+ text-decoration: underline;
451
+ }
452
+
453
+ .expandedIssueNumber {
454
+ color: var(--fgColor-muted, #656d76);
455
+ }
456
+
457
+ .expandedByline {
458
+ display: flex;
459
+ align-items: center;
460
+ gap: 8px;
461
+ font-size: 13px;
462
+ }
463
+
464
+ .expandedAuthor {
465
+ display: flex;
466
+ align-items: center;
467
+ gap: 6px;
468
+ text-decoration: none;
469
+ color: var(--fgColor-default, #1f2328);
470
+ font-weight: 600;
471
+ }
472
+
473
+ .expandedAuthor:hover {
474
+ text-decoration: underline;
475
+ }
476
+
477
+ .expandedBylineText {
478
+ color: var(--fgColor-muted, #656d76);
479
+ }
480
+
481
+ .expandedIssueBody {
482
+ flex: 1;
483
+ overflow: auto;
484
+ padding: 24px 40px;
485
+ font-size: 15px;
486
+ line-height: 1.7;
487
+ color: var(--fgColor-default, #1f2328);
488
+ max-width: 800px;
489
+ }
490
+
491
+ .expandedIssueBody * { pointer-events: auto; }
492
+ .expandedIssueBody a { color: var(--fgColor-accent, #0969da); text-decoration: none; }
493
+ .expandedIssueBody a:hover { text-decoration: underline; }
494
+ .expandedIssueBody img { max-width: 100%; height: auto; border-radius: 6px; margin: 8px 0; display: block; }
495
+ .expandedIssueBody video { max-width: 100%; height: auto; border-radius: 6px; margin: 8px 0; display: block; }
496
+ .expandedIssueBody h1 { font-size: 20px; font-weight: 700; margin: 16px 0 8px; border-bottom: 1px solid var(--borderColor-muted, #d8dee4); padding-bottom: 4px; }
497
+ .expandedIssueBody h2 { font-size: 17px; font-weight: 600; margin: 14px 0 6px; }
498
+ .expandedIssueBody h3 { font-size: 15px; font-weight: 600; margin: 12px 0 4px; }
499
+ .expandedIssueBody p { margin: 0 0 12px; }
500
+ .expandedIssueBody code { background: var(--bgColor-neutral-muted, #afb8c133); padding: 2px 5px; border-radius: 4px; font-size: 13px; font-family: ui-monospace, SFMono-Regular, monospace; }
501
+ .expandedIssueBody pre { padding: 12px 16px; border-radius: 6px; border: 1px solid var(--borderColor-muted, #d8dee4); overflow-x: auto; margin: 12px 0; background: var(--bgColor-neutral-muted, #afb8c133); }
502
+ .expandedIssueBody pre code { background: none; padding: 0; }
503
+ .expandedIssueBody ul { margin: 0 0 12px; padding-left: 24px; list-style: disc; }
504
+ .expandedIssueBody ol { margin: 0 0 12px; padding-left: 24px; list-style: decimal; }
505
+ .expandedIssueBody li { margin: 0 0 4px; display: list-item; }
506
+ .expandedIssueBody blockquote { border-left: 4px solid var(--borderColor-default, #d0d7de); margin: 12px 0; padding: 4px 16px; color: var(--fgColor-muted, #656d76); }
507
+
508
+ /* ── Expanded plain link view ─────────────────────────────────────── */
509
+
510
+ .expandedLink {
511
+ padding: 32px 40px;
512
+ font-family: var(--tc-font-stack, system-ui, -apple-system, sans-serif);
513
+ }
514
+
515
+ .expandedOgImage {
516
+ max-width: 100%;
517
+ max-height: 400px;
518
+ object-fit: cover;
519
+ border-radius: 8px;
520
+ margin-bottom: 16px;
521
+ display: block;
522
+ }
523
+
524
+ .expandedTitle {
525
+ margin: 0 0 8px;
526
+ font-size: 24px;
527
+ font-weight: 600;
528
+ color: var(--fgColor-default, #1f2328);
529
+ }
530
+
531
+ .expandedDescription {
532
+ margin: 0 0 12px;
533
+ font-size: 15px;
534
+ line-height: 1.5;
535
+ color: var(--fgColor-muted, #656d76);
536
+ }
537
+
538
+ .expandedUrl {
539
+ font-size: 14px;
540
+ color: var(--fgColor-accent, #0969da);
541
+ text-decoration: none;
542
+ }
543
+
544
+ .expandedUrl:hover {
545
+ text-decoration: underline;
546
+ }
@@ -1,4 +1,4 @@
1
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react'
1
+ import { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react'
2
2
  import { remark } from 'remark'
3
3
  import remarkGfm from 'remark-gfm'
4
4
  import remarkHtml from 'remark-html'
@@ -6,6 +6,7 @@ import WidgetWrapper from './WidgetWrapper.jsx'
6
6
  import ResizeHandle from './ResizeHandle.jsx'
7
7
  import { readProp } from './widgetProps.js'
8
8
  import { schemas } from './widgetConfig.js'
9
+ import SplitExpandModal from './SplitExpandModal.jsx'
9
10
  import styles from './MarkdownBlock.module.css'
10
11
 
11
12
  const markdownSchema = schemas['markdown']
@@ -64,18 +65,26 @@ async function highlightCodeBlocks(html) {
64
65
  )
65
66
  }
66
67
 
67
- export default function MarkdownBlock({ props, onUpdate, resizable }) {
68
+ export default forwardRef(function MarkdownBlock({ id, props, onUpdate, resizable }, ref) {
68
69
  const content = readProp(props, 'content', markdownSchema)
69
70
  const width = readProp(props, 'width', markdownSchema)
70
71
  const height = props?.height
71
72
  const collapsed = !!props?.collapsed
72
73
  const canEdit = typeof onUpdate === 'function'
73
74
  const [editing, setEditing] = useState(false)
75
+ const [expanded, setExpanded] = useState(false)
74
76
  const editingActive = canEdit && editing
75
77
  const textareaRef = useRef(null)
76
78
  const blockRef = useRef(null)
77
79
  const [editHeight, setEditHeight] = useState(null)
78
80
 
81
+ useImperativeHandle(ref, () => ({
82
+ handleAction(actionId) {
83
+ if (actionId === 'expand' || actionId === 'split-screen') { setExpanded(true); return true }
84
+ return false
85
+ },
86
+ }), [])
87
+
79
88
  const handleResize = useCallback((w, h) => {
80
89
  onUpdate?.({ width: w, height: h })
81
90
  }, [onUpdate])
@@ -115,12 +124,17 @@ export default function MarkdownBlock({ props, onUpdate, resizable }) {
115
124
  }
116
125
  }, [canEdit, content])
117
126
 
127
+ const startEditing = useCallback(() => {
128
+ // Capture the preview height BEFORE React swaps to the textarea
129
+ if (blockRef.current) {
130
+ setEditHeight(blockRef.current.offsetHeight)
131
+ blockRef.current.dataset.scrollTop = blockRef.current.scrollTop
132
+ }
133
+ setEditing(true)
134
+ }, [])
135
+
118
136
  useEffect(() => {
119
137
  if (editingActive) {
120
- // Capture the preview height before switching to editor
121
- if (blockRef.current && !editHeight) {
122
- setEditHeight(blockRef.current.offsetHeight)
123
- }
124
138
  if (textareaRef.current) {
125
139
  // Place cursor at end and prevent scroll jump to top
126
140
  const len = textareaRef.current.value.length
@@ -134,9 +148,10 @@ export default function MarkdownBlock({ props, onUpdate, resizable }) {
134
148
  } else {
135
149
  setEditHeight(null)
136
150
  }
137
- }, [editingActive, editHeight])
151
+ }, [editingActive])
138
152
 
139
153
  return (
154
+ <>
140
155
  <WidgetWrapper>
141
156
  <div
142
157
  ref={blockRef}
@@ -170,18 +185,11 @@ export default function MarkdownBlock({ props, onUpdate, resizable }) {
170
185
  data-canvas-allow-text-selection={!canEdit ? '' : undefined}
171
186
  onClick={!canEdit ? (e) => e.stopPropagation() : undefined}
172
187
  onCopy={!canEdit ? handleReadOnlyCopy : undefined}
173
- onDoubleClick={canEdit ? () => {
174
- // Save scroll position before switching to editor
175
- if (blockRef.current) blockRef.current.dataset.scrollTop = blockRef.current.scrollTop
176
- setEditing(true)
177
- } : undefined}
188
+ onDoubleClick={canEdit ? startEditing : undefined}
178
189
  role={canEdit ? 'button' : undefined}
179
190
  tabIndex={canEdit ? 0 : undefined}
180
191
  onKeyDown={canEdit ? (e) => {
181
- if (e.key === 'Enter') {
182
- if (blockRef.current) blockRef.current.dataset.scrollTop = blockRef.current.scrollTop
183
- setEditing(true)
184
- }
192
+ if (e.key === 'Enter') startEditing()
185
193
  } : undefined}
186
194
  dangerouslySetInnerHTML={{
187
195
  __html: renderedHtml || (canEdit
@@ -200,5 +208,19 @@ export default function MarkdownBlock({ props, onUpdate, resizable }) {
200
208
  )}
201
209
  </div>
202
210
  </WidgetWrapper>
211
+ <SplitExpandModal
212
+ expanded={expanded}
213
+ onClose={() => setExpanded(false)}
214
+ widgetId={id}
215
+ title="Markdown"
216
+ >
217
+ <div
218
+ className={styles.expandedPreview}
219
+ dangerouslySetInnerHTML={{
220
+ __html: renderedHtml || '<p>No content</p>',
221
+ }}
222
+ />
223
+ </SplitExpandModal>
224
+ </>
203
225
  )
204
- }
226
+ })
@@ -227,3 +227,126 @@
227
227
  color: var(--sb--markdown-fg);
228
228
  resize: none;
229
229
  }
230
+
231
+ /* ── Expanded preview in modal ──────────────────────────────────── */
232
+
233
+ .expandedPreview {
234
+ padding: 32px 40px;
235
+ font-size: 15px;
236
+ line-height: 1.7;
237
+ color: var(--sb--markdown-fg);
238
+ font-family: var(--tc-font-stack, system-ui, -apple-system, sans-serif);
239
+ max-width: 800px;
240
+ margin: 0 auto;
241
+ }
242
+
243
+ .expandedPreview * {
244
+ pointer-events: auto;
245
+ color: inherit;
246
+ }
247
+
248
+ .expandedPreview a {
249
+ color: var(--sb--markdown-accent);
250
+ text-decoration: none;
251
+ cursor: pointer;
252
+ }
253
+
254
+ .expandedPreview a:hover {
255
+ text-decoration: underline;
256
+ }
257
+
258
+ .expandedPreview img {
259
+ max-width: 100%;
260
+ height: auto;
261
+ border-radius: 6px;
262
+ border: 1px solid var(--borderColor-default, #d0d7de);
263
+ margin: 8px 0;
264
+ display: block;
265
+ }
266
+
267
+ .expandedPreview video {
268
+ max-width: 100%;
269
+ height: auto;
270
+ border-radius: 6px;
271
+ border: 1px solid var(--borderColor-default, #d0d7de);
272
+ margin: 8px 0;
273
+ display: block;
274
+ }
275
+
276
+ .expandedPreview h1 { font-size: 24px; font-weight: 700; margin: 0 0 12px; line-height: 1.3; }
277
+ .expandedPreview h2 { font-size: 20px; font-weight: 600; margin: 0 0 10px; line-height: 1.3; }
278
+ .expandedPreview h3 { font-size: 17px; font-weight: 600; margin: 0 0 6px; line-height: 1.3; }
279
+ .expandedPreview p { margin: 0 0 12px; }
280
+
281
+ .expandedPreview code {
282
+ background: var(--bgColor-neutral-muted, #afb8c133);
283
+ padding: 2px 5px;
284
+ border-radius: 4px;
285
+ font-size: 13px;
286
+ font-family: ui-monospace, SFMono-Regular, monospace;
287
+ }
288
+
289
+ .expandedPreview pre {
290
+ padding: 12px 16px;
291
+ border-radius: 6px;
292
+ border: 1px solid var(--borderColor-muted, #d8dee4);
293
+ overflow-x: auto;
294
+ margin: 8px 0;
295
+ background: var(--bgColor-neutral-muted, #afb8c133);
296
+ line-height: 1.4;
297
+ }
298
+
299
+ .expandedPreview pre code {
300
+ background: none;
301
+ padding: 0;
302
+ font-size: 13px;
303
+ white-space: pre;
304
+ display: block;
305
+ }
306
+
307
+ .expandedPreview ul { margin: 0 0 12px; padding-left: 24px; list-style: disc; }
308
+ .expandedPreview ol { margin: 0 0 12px; padding-left: 24px; list-style: decimal; }
309
+ .expandedPreview li { margin: 0 0 4px; display: list-item; }
310
+
311
+ .expandedPreview blockquote {
312
+ border-left: 4px solid var(--borderColor-default, #d0d7de);
313
+ margin: 12px 0;
314
+ padding: 4px 16px;
315
+ color: var(--sb--markdown-muted);
316
+ }
317
+
318
+ .expandedPreview table {
319
+ border-collapse: collapse;
320
+ margin: 12px 0;
321
+ width: 100%;
322
+ font-size: 14px;
323
+ }
324
+
325
+ .expandedPreview th,
326
+ .expandedPreview td {
327
+ border: 1px solid var(--borderColor-default, #d0d7de);
328
+ padding: 6px 12px;
329
+ text-align: left;
330
+ }
331
+
332
+ .expandedPreview th {
333
+ background: var(--bgColor-muted, #f6f8fa);
334
+ font-weight: 600;
335
+ }
336
+
337
+ .expandedPreview hr {
338
+ border: none;
339
+ border-top: 1px solid var(--borderColor-default, #d0d7de);
340
+ margin: 16px 0;
341
+ }
342
+
343
+ .expandedPreview input[type="checkbox"] {
344
+ margin-right: 6px;
345
+ pointer-events: none;
346
+ accent-color: var(--sb--markdown-accent);
347
+ }
348
+
349
+ .expandedPreview li:has(input[type="checkbox"]) {
350
+ list-style: none;
351
+ margin-left: -24px;
352
+ }