@bycrux/editor 0.4.5 → 0.4.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bycrux/editor",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -122,14 +122,14 @@ export default function AddElementMenu({ project, selectedSlideId, adapter, onAd
122
122
 
123
123
  return (
124
124
  <div className="flex flex-col gap-2">
125
- <div className="flex items-center gap-2">
125
+ <div className="grid grid-cols-2 gap-2">
126
126
  {adapter.generateImage && (
127
127
  <Button
128
128
  size="sm"
129
129
  variant="outline"
130
130
  disabled={disabled}
131
131
  onClick={() => { setShowPrompt(p => !p); setGenError(null) }}
132
- className="text-xs"
132
+ className="text-xs w-full justify-center whitespace-nowrap"
133
133
  >
134
134
  + AI Image
135
135
  </Button>
@@ -139,7 +139,7 @@ export default function AddElementMenu({ project, selectedSlideId, adapter, onAd
139
139
  variant="outline"
140
140
  disabled={disabled}
141
141
  onClick={() => fileInputRef.current?.click()}
142
- className="text-xs"
142
+ className="text-xs w-full justify-center whitespace-nowrap"
143
143
  >
144
144
  + Upload Image
145
145
  </Button>
@@ -156,7 +156,7 @@ export default function AddElementMenu({ project, selectedSlideId, adapter, onAd
156
156
  variant="outline"
157
157
  disabled={disabled || addingText}
158
158
  onClick={handleAddText}
159
- className="text-xs"
159
+ className="text-xs w-full justify-center whitespace-nowrap"
160
160
  >
161
161
  {addingText ? 'Adding…' : '+ Text'}
162
162
  </Button>
@@ -166,7 +166,7 @@ export default function AddElementMenu({ project, selectedSlideId, adapter, onAd
166
166
  variant="outline"
167
167
  disabled={disabled}
168
168
  onClick={() => setShowOverlayPicker(true)}
169
- className="text-xs"
169
+ className="text-xs w-full justify-center whitespace-nowrap"
170
170
  >
171
171
  + Overlay
172
172
  </Button>
@@ -70,7 +70,7 @@ function SlideGrid({
70
70
  }
71
71
 
72
72
  return (
73
- <div className="w-56 flex-shrink-0 flex flex-col border-r border-[var(--editor-border)] bg-[var(--editor-bg)] overflow-y-auto">
73
+ <div className="w-56 flex-shrink-0 flex flex-col border-r border-[var(--editor-border)] bg-[var(--editor-bg)] overflow-y-auto min-h-0 h-full">
74
74
  <div className="px-3 py-2 border-b border-[var(--editor-border)]">
75
75
  <span className="text-xs font-semibold text-[var(--editor-text)]/60 uppercase tracking-wider">Slides</span>
76
76
  </div>
@@ -365,10 +365,11 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
365
365
 
366
366
  return (
367
367
  <div ref={containerRef} className="flex flex-col h-full overflow-y-auto bg-[var(--editor-bg)]">
368
- {/* TOP: slide rail | canvas | editing panel (right). Given a generous
369
- viewport-relative height so the slide renders large; the project-media
370
- region flows beneath and the whole editor scrolls vertically. */}
371
- <div className="flex flex-shrink-0 min-h-[62vh] overflow-hidden">
368
+ {/* TOP: slide rail | canvas | editing panel (right). Fixed viewport-relative
369
+ height with min-h-0 so each of the three columns establishes its own
370
+ independent scroll context; the project-media region flows beneath and
371
+ the whole editor scrolls vertically. */}
372
+ <div className="flex flex-shrink-0 h-[78vh] min-h-0 overflow-hidden">
372
373
  <SlideGrid
373
374
  project={project}
374
375
  slides={slides}
@@ -382,40 +383,49 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
382
383
  compileOverlay={(t) => adapter.compileOverlay(t)}
383
384
  />
384
385
 
385
- <div ref={canvasContainerRef} className="relative flex-1 flex flex-col items-center justify-center gap-4 overflow-hidden p-4">
386
- <button
387
- onClick={handleRefresh}
388
- disabled={refreshing}
389
- className={`absolute top-3 left-3 z-30 flex items-center gap-2 px-3 py-2 rounded-md border transition-colors ${
390
- refreshState === 'err'
391
- ? 'text-red-300 border-red-500/40 bg-red-950/60 hover:bg-red-900/70'
392
- : 'text-[var(--editor-text)] border-[var(--editor-border)] bg-[var(--editor-surface)]/80 hover:text-[var(--editor-text)] hover:border-[var(--editor-accent)] hover:bg-[var(--editor-surface)]'
393
- }`}
394
- title={refreshState === 'err' ? 'Refresh failed — check connection' : 'Refresh project'}
395
- >
396
- {refreshState === 'err' ? <AlertCircle size={18} /> : <RefreshCw size={18} className={refreshing ? 'animate-spin' : ''} />}
397
- <span className="text-xs font-medium">Refresh</span>
398
- </button>
399
-
400
- <div className="absolute top-3 right-3 z-30 flex items-center gap-2">
401
- {slots?.toolbarActions}
386
+ {/* CANVAS COLUMN: a pinned toolbar row on top, then the independently
387
+ scrolling slide-rendering area below it. */}
388
+ <div className="relative flex-1 flex flex-col min-h-0 overflow-hidden">
389
+ {/* TOOLBAR ROW: Refresh on the left; host toolbar actions + Render on the
390
+ right. Pinned (shrink-0) above the scrolling canvas area. */}
391
+ <div className="flex-shrink-0 flex items-center justify-between gap-2 px-4 py-3 border-b border-[var(--editor-border)]">
402
392
  <button
403
- onClick={handleRender}
404
- disabled={rendering || project.status === 'pending' || slides.length === 0}
405
- className="flex items-center gap-2 px-3 py-2 rounded-md border border-[var(--editor-accent)] bg-[var(--editor-accent)] text-[var(--editor-accent-foreground)] hover:opacity-90 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
406
- title={
407
- project.status === 'pending'
408
- ? 'Wait for the agent to finish before rendering'
409
- : slides.length === 0
410
- ? 'Add slides before rendering'
411
- : 'Render all slides as PNGs'
412
- }
393
+ onClick={handleRefresh}
394
+ disabled={refreshing}
395
+ className={`flex items-center gap-2 px-3 py-2 rounded-md border transition-colors ${
396
+ refreshState === 'err'
397
+ ? 'text-red-300 border-red-500/40 bg-red-950/60 hover:bg-red-900/70'
398
+ : 'text-[var(--editor-text)] border-[var(--editor-border)] bg-[var(--editor-surface)]/80 hover:text-[var(--editor-text)] hover:border-[var(--editor-accent)] hover:bg-[var(--editor-surface)]'
399
+ }`}
400
+ title={refreshState === 'err' ? 'Refresh failed check connection' : 'Refresh project'}
413
401
  >
414
- <Download size={18} />
415
- <span className="text-xs font-medium">{rendering ? 'Starting…' : 'Render'}</span>
402
+ {refreshState === 'err' ? <AlertCircle size={18} /> : <RefreshCw size={18} className={refreshing ? 'animate-spin' : ''} />}
403
+ <span className="text-xs font-medium">Refresh</span>
416
404
  </button>
405
+
406
+ <div className="flex items-center gap-2">
407
+ {slots?.toolbarActions}
408
+ <button
409
+ onClick={handleRender}
410
+ disabled={rendering || project.status === 'pending' || slides.length === 0}
411
+ className="flex items-center gap-2 px-3 py-2 rounded-md border border-[var(--editor-accent)] bg-[var(--editor-accent)] text-[var(--editor-accent-foreground)] hover:opacity-90 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
412
+ title={
413
+ project.status === 'pending'
414
+ ? 'Wait for the agent to finish before rendering'
415
+ : slides.length === 0
416
+ ? 'Add slides before rendering'
417
+ : 'Render all slides as PNGs'
418
+ }
419
+ >
420
+ <Download size={18} />
421
+ <span className="text-xs font-medium">{rendering ? 'Starting…' : 'Render'}</span>
422
+ </button>
423
+ </div>
417
424
  </div>
418
425
 
426
+ {/* SCROLL AREA: the slide viewport. ResizeObserver lives here so
427
+ canvasScale measures the slide-rendering area, not the toolbar. */}
428
+ <div ref={canvasContainerRef} className="relative flex-1 flex flex-col items-center justify-center gap-4 overflow-y-auto min-h-0 p-4">
419
429
  {project.status === 'pending' ? (
420
430
  <div className="flex flex-col items-center gap-6 text-center max-w-lg w-full">
421
431
  {slots?.pendingStatus ?? (
@@ -493,10 +503,11 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
493
503
  </div>
494
504
  )}
495
505
  </div>
506
+ </div>
496
507
 
497
508
  {/* RIGHT: the slide editor (add-element toolbar + property panel),
498
- beside the canvas with its own vertical scroll. */}
499
- <div className="w-[24rem] flex-shrink-0 border-l border-[var(--editor-border)] flex flex-col overflow-y-auto bg-[var(--editor-bg)]">
509
+ beside the canvas with its own independent vertical scroll. */}
510
+ <div className="w-[24rem] flex-shrink-0 border-l border-[var(--editor-border)] flex flex-col overflow-y-auto min-h-0 h-full bg-[var(--editor-bg)]">
500
511
  {selectedSlide && project.status !== 'pending' && (
501
512
  <div className="px-4 py-2 border-b border-[var(--editor-border)]">
502
513
  <AddElementMenu
@@ -342,11 +342,12 @@ export default function SlideCanvas({
342
342
  data-testid="snap-guide-x"
343
343
  style={{
344
344
  position: 'absolute',
345
- left: g.at * scale - 0.5,
345
+ left: g.at * scale - 1,
346
346
  top: 0,
347
- width: 1,
347
+ width: 2,
348
348
  height: displayH,
349
- background: '#ec4899',
349
+ background: '#ff2d88',
350
+ boxShadow: '0 0 6px 1px rgba(255,45,136,0.9)',
350
351
  pointerEvents: 'none',
351
352
  zIndex: 999,
352
353
  }}
@@ -358,10 +359,11 @@ export default function SlideCanvas({
358
359
  style={{
359
360
  position: 'absolute',
360
361
  left: 0,
361
- top: g.at * scale - 0.5,
362
+ top: g.at * scale - 1,
362
363
  width: displayW,
363
- height: 1,
364
- background: '#ec4899',
364
+ height: 2,
365
+ background: '#ff2d88',
366
+ boxShadow: '0 0 6px 1px rgba(255,45,136,0.9)',
365
367
  pointerEvents: 'none',
366
368
  zIndex: 999,
367
369
  }}