@zuilib/text-editor 0.4.0 → 0.5.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/styles.css CHANGED
@@ -350,17 +350,25 @@
350
350
  margin: 1rem 0;
351
351
  }
352
352
 
353
- .zui-drawing-canvas.is-content-width {
354
- width: fit-content;
355
- max-width: 100%;
356
- }
357
-
358
353
  .zui-drawing-canvas {
354
+ --zui-drawing-accent: #6965db;
359
355
  position: relative;
360
- border-radius: 0.75rem;
356
+ border: 1px solid color-mix(in srgb, currentColor 10%, transparent);
357
+ border-radius: 0.875rem;
361
358
  background: #ffffff;
362
359
  outline: none;
363
360
  overflow: hidden;
361
+ transition: box-shadow 0.18s ease, border-color 0.18s ease;
362
+ }
363
+
364
+ .zui-drawing-canvas.is-editable:focus-within {
365
+ border-color: color-mix(in srgb, var(--zui-drawing-accent) 45%, transparent);
366
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--zui-drawing-accent) 12%, transparent);
367
+ }
368
+
369
+ .zui-drawing-canvas.is-content-width {
370
+ width: fit-content;
371
+ max-width: 100%;
364
372
  }
365
373
 
366
374
  .zui-drawing-surface {
@@ -369,121 +377,242 @@
369
377
  max-width: 100%;
370
378
  touch-action: none;
371
379
  background-color: #ffffff;
372
- background-image: radial-gradient(
373
- circle,
374
- rgba(0, 0, 0, 0.07) 1px,
375
- transparent 1px
376
- );
380
+ background-image: radial-gradient(circle, rgba(0, 0, 0, 0.09) 1px, transparent 1px);
377
381
  background-size: 20px 20px;
378
382
  }
379
383
 
380
- /*
381
- * Dark mode (zui `.dark` class convention). The drawing surface, the color
382
- * swatches, and the inline text input are darkened Excalidraw-style with a
383
- * color-inversion filter, so stored shape colors stay theme-agnostic: the
384
- * default near-black stroke renders light on the dark canvas, and the
385
- * swatches preview exactly what will be drawn.
386
- */
387
- .dark .zui-drawing-canvas {
388
- background: #18181b;
389
- }
390
-
391
- .dark .zui-drawing-surface,
392
- .dark .zui-drawing-swatch,
393
- .dark .zui-drawing-text-input {
394
- filter: invert(93%) hue-rotate(180deg);
384
+ .zui-drawing-shape {
385
+ transition: filter 0.12s ease;
395
386
  }
396
387
 
397
- .dark .zui-drawing-toolbar-group {
398
- background: #232329;
399
- border-color: rgba(255, 255, 255, 0.1);
400
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
388
+ .zui-drawing-canvas.is-editable .zui-drawing-shape:hover:not(.is-selected) {
389
+ filter: drop-shadow(0 0 2.5px color-mix(in srgb, var(--zui-drawing-accent) 55%, transparent));
401
390
  }
402
391
 
403
- .dark .zui-drawing-tool {
404
- color: #e3e3e8;
392
+ .zui-drawing-handle {
393
+ filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.25));
405
394
  }
406
395
 
407
- .dark .zui-drawing-tool:hover {
408
- background: rgba(255, 255, 255, 0.08);
396
+ .zui-drawing-ghost {
397
+ opacity: 0.55;
398
+ transition: opacity 0.12s ease;
409
399
  }
410
400
 
411
- .dark .zui-drawing-tool.is-active {
412
- background: #45437a;
413
- color: #dcdaff;
401
+ .zui-drawing-ghost:hover {
402
+ opacity: 1;
414
403
  }
415
404
 
416
- .dark .zui-drawing-swatch-label {
417
- color: rgba(255, 255, 255, 0.55);
405
+ .zui-drawing-bind-target {
406
+ animation: zui-drawing-pulse 0.9s ease-in-out infinite alternate;
418
407
  }
419
408
 
420
- .dark .zui-drawing-resize {
421
- background: rgba(255, 255, 255, 0.2);
409
+ @keyframes zui-drawing-pulse {
410
+ from {
411
+ stroke-opacity: 0.55;
412
+ }
413
+ to {
414
+ stroke-opacity: 1;
415
+ }
422
416
  }
423
417
 
424
- .dark .zui-drawing-resize:hover {
425
- background: rgba(255, 255, 255, 0.35);
418
+ @keyframes zui-drawing-pop {
419
+ from {
420
+ opacity: 0;
421
+ transform: translateY(4px) scale(0.98);
422
+ }
423
+ to {
424
+ opacity: 1;
425
+ transform: translateY(0) scale(1);
426
+ }
426
427
  }
427
428
 
429
+ /* Floating toolbar */
428
430
  .zui-drawing-toolbar {
429
431
  position: absolute;
430
- top: 0.5rem;
431
- left: 0.5rem;
432
- right: 0.5rem;
432
+ top: 0.625rem;
433
+ left: 0.625rem;
434
+ right: 0.625rem;
433
435
  z-index: 5;
434
436
  display: flex;
435
437
  flex-wrap: wrap;
436
- align-items: center;
438
+ align-items: flex-start;
437
439
  gap: 0.5rem;
438
440
  pointer-events: none;
439
441
  }
440
442
 
441
- .zui-drawing-toolbar-group {
443
+ .zui-drawing-toolbar-group,
444
+ .zui-drawing-props {
442
445
  display: flex;
443
446
  align-items: center;
444
447
  gap: 0.125rem;
445
448
  padding: 0.25rem;
446
- border: 1px solid rgba(0, 0, 0, 0.08);
447
- border-radius: 0.5rem;
448
- background: #ffffff;
449
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
449
+ border: 1px solid rgba(0, 0, 0, 0.07);
450
+ border-radius: 0.625rem;
451
+ background: rgba(255, 255, 255, 0.88);
452
+ backdrop-filter: blur(10px);
453
+ -webkit-backdrop-filter: blur(10px);
454
+ box-shadow:
455
+ 0 1px 2px rgba(0, 0, 0, 0.05),
456
+ 0 6px 20px -8px rgba(0, 0, 0, 0.18);
450
457
  pointer-events: auto;
451
458
  }
452
459
 
453
- /* Canvas-level controls (width) sit at the far right of the toolbar row */
454
460
  .zui-drawing-toolbar-group-end {
455
461
  margin-left: auto;
456
462
  }
457
463
 
464
+ .zui-drawing-toolbar-divider {
465
+ width: 1px;
466
+ height: 1.125rem;
467
+ margin: 0 0.25rem;
468
+ background: rgba(0, 0, 0, 0.1);
469
+ }
470
+
458
471
  .zui-drawing-tool {
472
+ position: relative;
459
473
  display: inline-flex;
460
474
  align-items: center;
461
475
  justify-content: center;
462
- width: 1.75rem;
463
- height: 1.75rem;
476
+ width: 1.875rem;
477
+ height: 1.875rem;
464
478
  border: none;
465
- border-radius: 0.375rem;
479
+ border-radius: 0.5rem;
466
480
  background: transparent;
467
481
  color: #1e1e1e;
468
482
  cursor: pointer;
483
+ transition:
484
+ background-color 0.12s ease,
485
+ color 0.12s ease,
486
+ transform 0.12s ease;
469
487
  }
470
488
 
471
489
  .zui-drawing-tool:hover {
472
490
  background: rgba(0, 0, 0, 0.06);
473
491
  }
474
492
 
493
+ .zui-drawing-tool:active {
494
+ transform: scale(0.94);
495
+ }
496
+
475
497
  .zui-drawing-tool.is-active {
476
- background: #e0dfff;
498
+ background: color-mix(in srgb, var(--zui-drawing-accent) 16%, transparent);
477
499
  color: #4a47b1;
478
500
  }
479
501
 
502
+ .zui-drawing-tool.is-success {
503
+ color: #2f9e44;
504
+ }
505
+
480
506
  .zui-drawing-tool-danger {
481
507
  color: #e03131;
482
508
  }
483
509
 
510
+ .zui-drawing-tool-danger:hover {
511
+ background: rgba(224, 49, 49, 0.1);
512
+ }
513
+
514
+ .zui-drawing-tool-more {
515
+ width: 2.375rem;
516
+ }
517
+
518
+ .zui-drawing-caret {
519
+ position: absolute;
520
+ right: 0.3rem;
521
+ bottom: 0.3rem;
522
+ width: 0;
523
+ height: 0;
524
+ border-left: 4px solid transparent;
525
+ border-top: 4px solid currentColor;
526
+ opacity: 0.6;
527
+ }
528
+
529
+ .zui-drawing-more {
530
+ position: relative;
531
+ }
532
+
533
+ .zui-drawing-popover {
534
+ position: absolute;
535
+ top: calc(100% + 0.375rem);
536
+ left: 0;
537
+ z-index: 7;
538
+ display: flex;
539
+ flex-direction: column;
540
+ gap: 0.125rem;
541
+ min-width: 9.5rem;
542
+ padding: 0.25rem;
543
+ border: 1px solid rgba(0, 0, 0, 0.08);
544
+ border-radius: 0.625rem;
545
+ background: #ffffff;
546
+ box-shadow:
547
+ 0 1px 2px rgba(0, 0, 0, 0.06),
548
+ 0 10px 28px -8px rgba(0, 0, 0, 0.25);
549
+ animation: zui-drawing-pop 0.14s ease-out;
550
+ transform-origin: top left;
551
+ }
552
+
553
+ .zui-drawing-popover-item {
554
+ display: flex;
555
+ align-items: center;
556
+ gap: 0.5rem;
557
+ padding: 0.375rem 0.5rem;
558
+ border: none;
559
+ border-radius: 0.375rem;
560
+ background: transparent;
561
+ color: #1e1e1e;
562
+ font-size: 0.8125rem;
563
+ text-align: left;
564
+ cursor: pointer;
565
+ transition: background-color 0.12s ease;
566
+ }
567
+
568
+ .zui-drawing-popover-item:hover {
569
+ background: rgba(0, 0, 0, 0.06);
570
+ }
571
+
572
+ .zui-drawing-popover-item.is-active {
573
+ background: color-mix(in srgb, var(--zui-drawing-accent) 16%, transparent);
574
+ color: #4a47b1;
575
+ }
576
+
577
+ /* Contextual property bar: its own row under the tools */
578
+ .zui-drawing-props-host {
579
+ flex-basis: 100%;
580
+ display: flex;
581
+ opacity: 0;
582
+ transform: translateY(-4px);
583
+ pointer-events: none;
584
+ transition:
585
+ opacity 0.16s ease,
586
+ transform 0.16s ease;
587
+ }
588
+
589
+ .zui-drawing-props-host.is-visible {
590
+ opacity: 1;
591
+ transform: translateY(0);
592
+ pointer-events: auto;
593
+ }
594
+
595
+ .zui-drawing-props {
596
+ flex-wrap: wrap;
597
+ gap: 0.25rem;
598
+ padding: 0.25rem 0.375rem;
599
+ }
600
+
601
+ .zui-drawing-props-group {
602
+ display: flex;
603
+ align-items: center;
604
+ gap: 0.125rem;
605
+ }
606
+
607
+ .zui-drawing-props-group + .zui-drawing-props-group {
608
+ padding-left: 0.375rem;
609
+ border-left: 1px solid rgba(0, 0, 0, 0.1);
610
+ }
611
+
484
612
  .zui-drawing-swatch-label {
485
- margin: 0 0.25rem;
613
+ margin: 0 0.25rem 0 0.125rem;
486
614
  font-size: 0.6875rem;
615
+ letter-spacing: 0.02em;
487
616
  color: rgba(0, 0, 0, 0.5);
488
617
  user-select: none;
489
618
  }
@@ -491,58 +620,173 @@
491
620
  .zui-drawing-swatch {
492
621
  width: 1.125rem;
493
622
  height: 1.125rem;
494
- margin: 0 0.0625rem;
495
- border: 1px solid rgba(0, 0, 0, 0.15);
496
- border-radius: 0.3125rem;
497
- cursor: pointer;
623
+ margin: 0 0.125rem;
498
624
  padding: 0;
625
+ border: 1px solid rgba(0, 0, 0, 0.14);
626
+ border-radius: 999px;
627
+ cursor: pointer;
628
+ transition:
629
+ transform 0.12s ease,
630
+ box-shadow 0.12s ease;
631
+ }
632
+
633
+ .zui-drawing-swatch:hover {
634
+ transform: scale(1.12);
499
635
  }
500
636
 
501
637
  .zui-drawing-swatch.is-active {
502
- outline: 2px solid #6965db;
503
- outline-offset: 1px;
638
+ box-shadow:
639
+ 0 0 0 2px #ffffff,
640
+ 0 0 0 3.5px var(--zui-drawing-accent);
504
641
  }
505
642
 
506
643
  .zui-drawing-swatch.is-transparent {
507
644
  background-image: linear-gradient(
508
645
  45deg,
509
- transparent 46%,
510
- #e03131 46%,
511
- #e03131 54%,
512
- transparent 54%
646
+ transparent 44%,
647
+ #e03131 44%,
648
+ #e03131 56%,
649
+ transparent 56%
513
650
  );
514
651
  }
515
652
 
653
+ /* Empty state */
654
+ .zui-drawing-empty {
655
+ position: absolute;
656
+ inset: 0;
657
+ display: flex;
658
+ align-items: center;
659
+ justify-content: center;
660
+ padding-top: 2rem;
661
+ font-size: 0.8125rem;
662
+ color: rgba(0, 0, 0, 0.4);
663
+ pointer-events: none;
664
+ user-select: none;
665
+ }
666
+
667
+ /* Inline text editing */
668
+ .zui-drawing-overlay {
669
+ position: absolute;
670
+ top: 0;
671
+ left: 0;
672
+ right: 0;
673
+ bottom: 0;
674
+ transform-origin: top left;
675
+ pointer-events: none;
676
+ }
677
+
516
678
  .zui-drawing-text-input {
517
679
  position: absolute;
518
680
  z-index: 6;
519
- padding: 2px 4px;
520
- border: 1px dashed #6965db;
521
- border-radius: 0.25rem;
522
- background: rgba(255, 255, 255, 0.9);
523
- font-family: ui-sans-serif, system-ui, sans-serif;
524
- line-height: 1.35;
681
+ margin: 0;
682
+ padding: 0;
683
+ border: 0;
684
+ border-radius: 3px;
685
+ outline: 1.5px dashed var(--zui-drawing-accent, #6965db);
686
+ outline-offset: 3px;
687
+ background: rgba(255, 255, 255, 0.85);
688
+ color: inherit;
525
689
  resize: none;
526
- outline: none;
527
690
  overflow: hidden;
528
- white-space: pre;
691
+ box-sizing: content-box;
692
+ pointer-events: auto;
529
693
  }
530
694
 
695
+ .zui-drawing-text-input::placeholder {
696
+ color: currentColor;
697
+ opacity: 0.35;
698
+ }
699
+
700
+ /* Canvas height grip (bottom-right) */
531
701
  .zui-drawing-resize {
532
702
  position: absolute;
533
- bottom: 0.25rem;
534
- left: 50%;
535
- transform: translateX(-50%);
536
- width: 3rem;
537
- height: 0.375rem;
538
- border-radius: 999px;
539
- background: rgba(0, 0, 0, 0.15);
703
+ right: 0.375rem;
704
+ bottom: 0.375rem;
705
+ display: inline-flex;
706
+ align-items: center;
707
+ justify-content: center;
708
+ width: 1.375rem;
709
+ height: 1.375rem;
710
+ border-radius: 0.375rem;
711
+ color: #1e1e1e;
712
+ opacity: 0.35;
540
713
  cursor: ns-resize;
541
714
  touch-action: none;
715
+ transition:
716
+ opacity 0.12s ease,
717
+ background-color 0.12s ease;
542
718
  }
543
719
 
544
720
  .zui-drawing-resize:hover {
545
- background: rgba(0, 0, 0, 0.3);
721
+ opacity: 0.9;
722
+ background: rgba(0, 0, 0, 0.06);
723
+ }
724
+
725
+ /*
726
+ * Dark mode (zui `.dark` class convention). The drawing surface, the color
727
+ * swatches, and the inline text input are darkened with a color-inversion
728
+ * filter, so stored shape colors stay theme-agnostic: the default
729
+ * near-black stroke renders light on the dark canvas, and the swatches
730
+ * preview exactly what will be drawn.
731
+ */
732
+ .dark .zui-drawing-canvas {
733
+ background: #18181b;
734
+ border-color: rgba(255, 255, 255, 0.1);
735
+ }
736
+
737
+ .dark .zui-drawing-surface,
738
+ .dark .zui-drawing-swatch,
739
+ .dark .zui-drawing-text-input {
740
+ filter: invert(93%) hue-rotate(180deg);
741
+ }
742
+
743
+ .dark .zui-drawing-toolbar-group,
744
+ .dark .zui-drawing-props,
745
+ .dark .zui-drawing-popover {
746
+ background: rgba(35, 35, 41, 0.9);
747
+ border-color: rgba(255, 255, 255, 0.1);
748
+ box-shadow:
749
+ 0 1px 2px rgba(0, 0, 0, 0.3),
750
+ 0 8px 24px -8px rgba(0, 0, 0, 0.6);
751
+ }
752
+
753
+ .dark .zui-drawing-tool,
754
+ .dark .zui-drawing-popover-item,
755
+ .dark .zui-drawing-resize {
756
+ color: #e3e3e8;
757
+ }
758
+
759
+ .dark .zui-drawing-tool:hover,
760
+ .dark .zui-drawing-popover-item:hover,
761
+ .dark .zui-drawing-resize:hover {
762
+ background: rgba(255, 255, 255, 0.08);
763
+ }
764
+
765
+ .dark .zui-drawing-tool.is-active,
766
+ .dark .zui-drawing-popover-item.is-active {
767
+ background: #45437a;
768
+ color: #dcdaff;
769
+ }
770
+
771
+ .dark .zui-drawing-toolbar-divider,
772
+ .dark .zui-drawing-props-group + .zui-drawing-props-group {
773
+ border-color: rgba(255, 255, 255, 0.12);
774
+ background-color: rgba(255, 255, 255, 0.12);
775
+ }
776
+
777
+ .dark .zui-drawing-props-group + .zui-drawing-props-group {
778
+ background-color: transparent;
779
+ }
780
+
781
+ .dark .zui-drawing-swatch-label,
782
+ .dark .zui-drawing-empty {
783
+ color: rgba(255, 255, 255, 0.55);
784
+ }
785
+
786
+ .dark .zui-drawing-swatch.is-active {
787
+ box-shadow:
788
+ 0 0 0 2px #232329,
789
+ 0 0 0 3.5px #8f8bff;
546
790
  }
547
791
 
548
792
  .zui-text-editor-textarea {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuilib/text-editor",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "ZUI — A markdown editor component wrapping Lexical",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsup",
18
- "test": "node tests/roundtrip.mjs"
18
+ "test": "node tests/roundtrip.mjs && node --test tests/*.test.mjs"
19
19
  },
20
20
  "exports": {
21
21
  ".": {