noteloom 0.1.6 → 0.2.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/style.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * noteloom's default theme — minimal, clean, Notion/Linear-adjacent.
2
+ * noteloom's default theme — minimal, clean, modern SaaS-adjacent.
3
3
  *
4
4
  * Ships as plain CSS custom properties on :root (not scoped to a wrapper
5
5
  * element) deliberately: several of this package's own pieces — Select's
@@ -101,6 +101,18 @@
101
101
  }
102
102
 
103
103
  .be-editable {
104
+ /* Block-level and full-width so the whole line (including the blank
105
+ trailing space after short text, and an empty line's full height) is
106
+ part of the actual contentEditable hit-test box. Left inline/sized to
107
+ content, a click on that blank area lands on the non-editable wrapper
108
+ div instead of this element: the browser's native "nothing focused
109
+ yet" resolution still finds it on a first click, but re-clicking the
110
+ same dead-zone point while this element already holds the selection
111
+ collapses/clears it instead, since there's nothing here to re-anchor
112
+ the caret onto. */
113
+ display: block;
114
+ width: 100%;
115
+ min-height: 1em;
104
116
  outline: none;
105
117
  }
106
118
 
@@ -753,6 +765,311 @@
753
765
  font-weight: 500;
754
766
  }
755
767
 
768
+ /* --- canvas (freehand drawing) --- */
769
+
770
+ .be-canvas {
771
+ position: relative;
772
+ display: inline-block;
773
+ margin: 0.6em 0;
774
+ line-height: 0; /* avoids extra inline-block whitespace gap below the <svg> */
775
+ }
776
+
777
+ .be-canvas-surface {
778
+ display: block;
779
+ border: 1px solid var(--noteloom-border);
780
+ border-radius: var(--noteloom-radius-lg);
781
+ background: var(--noteloom-bg);
782
+ max-width: 100%;
783
+ touch-action: none; /* the surface owns its own pointer gestures (drawing/panning), not the browser's default scroll/zoom */
784
+ }
785
+
786
+ .be-canvas-toolbar {
787
+ position: absolute;
788
+ top: 8px;
789
+ right: 8px;
790
+ z-index: 2;
791
+ display: flex;
792
+ flex-wrap: wrap;
793
+ justify-content: flex-end;
794
+ max-width: calc(100% - 16px);
795
+ align-items: center;
796
+ gap: 2px;
797
+ padding: 3px;
798
+ background: rgba(15, 23, 42, 0.72);
799
+ border-radius: var(--noteloom-radius-md);
800
+ opacity: 0;
801
+ transition: opacity 0.12s ease;
802
+ }
803
+
804
+ .be-canvas:hover .be-canvas-toolbar,
805
+ .be-canvas-toolbar:focus-within {
806
+ opacity: 1;
807
+ }
808
+
809
+ .be-canvas-toolbar-btn {
810
+ border: none;
811
+ background: transparent;
812
+ width: 28px;
813
+ height: 24px;
814
+ display: flex;
815
+ align-items: center;
816
+ justify-content: center;
817
+ color: rgba(255, 255, 255, 0.8);
818
+ border-radius: var(--noteloom-radius-sm);
819
+ cursor: pointer;
820
+ }
821
+
822
+ .be-canvas-toolbar-btn:hover {
823
+ background: rgba(255, 255, 255, 0.16);
824
+ color: #fff;
825
+ }
826
+
827
+ .be-canvas-toolbar-btn-active {
828
+ background: var(--noteloom-accent);
829
+ color: #fff;
830
+ }
831
+
832
+ .be-canvas-toolbar-btn:disabled {
833
+ opacity: 0.4;
834
+ cursor: default;
835
+ }
836
+
837
+ .be-canvas-toolbar-btn:disabled:hover {
838
+ background: transparent;
839
+ }
840
+
841
+ .be-canvas-zoom-label {
842
+ border: none;
843
+ background: transparent;
844
+ color: rgba(255, 255, 255, 0.8);
845
+ font-size: 0.75rem;
846
+ min-width: 2.8em;
847
+ padding: 0 2px;
848
+ cursor: pointer;
849
+ border-radius: var(--noteloom-radius-sm);
850
+ }
851
+
852
+ .be-canvas-zoom-label:hover {
853
+ background: rgba(255, 255, 255, 0.16);
854
+ color: #fff;
855
+ }
856
+
857
+ .be-canvas-toolbar-divider {
858
+ width: 1px;
859
+ height: 16px;
860
+ background: rgba(255, 255, 255, 0.24);
861
+ margin: 0 2px;
862
+ }
863
+
864
+ .be-canvas-toolbar-swatch {
865
+ border: 2px solid transparent;
866
+ width: 18px;
867
+ height: 18px;
868
+ border-radius: 50%;
869
+ cursor: pointer;
870
+ padding: 0;
871
+ }
872
+
873
+ .be-canvas-toolbar-swatch-active {
874
+ border-color: #fff;
875
+ }
876
+
877
+ .be-canvas-picker-wrap {
878
+ position: relative;
879
+ }
880
+
881
+ .be-canvas-picker {
882
+ position: absolute;
883
+ top: 100%;
884
+ right: 0;
885
+ margin-top: 8px;
886
+ display: flex;
887
+ align-items: center;
888
+ gap: 6px;
889
+ padding: 8px;
890
+ background: rgba(15, 23, 42, 0.92);
891
+ border-radius: var(--noteloom-radius-md);
892
+ z-index: 3;
893
+ }
894
+
895
+ .be-canvas-shape-picker {
896
+ flex-wrap: wrap;
897
+ max-width: 132px;
898
+ }
899
+
900
+ .be-canvas-color-trigger-dot {
901
+ display: block;
902
+ width: 14px;
903
+ height: 14px;
904
+ border-radius: 50%;
905
+ border: 1px solid rgba(255, 255, 255, 0.6);
906
+ }
907
+
908
+ .be-canvas-fill-trigger-dot {
909
+ display: block;
910
+ width: 14px;
911
+ height: 14px;
912
+ border-radius: 50%;
913
+ border: 1px solid rgba(255, 255, 255, 0.6);
914
+ }
915
+
916
+ .be-canvas-fill-trigger-dot-none,
917
+ .be-canvas-fill-swatch-none {
918
+ background: repeating-conic-gradient(rgba(255, 255, 255, 0.5) 0% 25%, transparent 0% 50%) 50% / 8px 8px;
919
+ }
920
+
921
+ .be-canvas-custom-swatch {
922
+ width: 18px;
923
+ height: 18px;
924
+ border-radius: 50%;
925
+ overflow: hidden;
926
+ border: 1px dashed rgba(255, 255, 255, 0.6);
927
+ display: inline-flex;
928
+ cursor: pointer;
929
+ flex-shrink: 0;
930
+ }
931
+
932
+ .be-canvas-custom-swatch input[type='color'] {
933
+ width: 150%;
934
+ height: 150%;
935
+ margin: -25%;
936
+ border: none;
937
+ padding: 0;
938
+ cursor: pointer;
939
+ background: none;
940
+ }
941
+
942
+ /* Live size-preview popover: dot grows/shrinks together with the slider as it's dragged. */
943
+ .be-canvas-size-picker {
944
+ min-width: 160px;
945
+ }
946
+
947
+ .be-canvas-size-trigger-dot {
948
+ display: block;
949
+ border-radius: 50%;
950
+ background: #fff;
951
+ }
952
+
953
+ .be-canvas-size-preview {
954
+ flex-shrink: 0;
955
+ width: 44px;
956
+ height: 44px;
957
+ display: flex;
958
+ align-items: center;
959
+ justify-content: center;
960
+ }
961
+
962
+ .be-canvas-size-preview-dot {
963
+ display: block;
964
+ border-radius: 50%;
965
+ background: #fff;
966
+ }
967
+
968
+ .be-canvas-size-slider {
969
+ flex: 1;
970
+ min-width: 80px;
971
+ }
972
+
973
+ .be-canvas-font-size-trigger {
974
+ display: block;
975
+ font-size: 13px;
976
+ font-weight: 600;
977
+ line-height: 1;
978
+ color: rgba(255, 255, 255, 0.85);
979
+ }
980
+
981
+ .be-canvas-font-size-preview-letter {
982
+ display: block;
983
+ line-height: 1;
984
+ color: #fff;
985
+ font-weight: 600;
986
+ }
987
+
988
+ .be-canvas-size-value {
989
+ flex-shrink: 0;
990
+ min-width: 2.5em;
991
+ text-align: right;
992
+ font-size: 0.8rem;
993
+ color: rgba(255, 255, 255, 0.85);
994
+ }
995
+
996
+ .be-canvas[data-tool='eraser'] .be-canvas-surface {
997
+ cursor: cell;
998
+ }
999
+
1000
+ .be-canvas[data-tool='select'] .be-canvas-surface {
1001
+ cursor: default;
1002
+ }
1003
+
1004
+ .be-canvas-selection-box {
1005
+ stroke: var(--noteloom-accent);
1006
+ stroke-width: 1.5;
1007
+ stroke-dasharray: 6 4;
1008
+ vector-effect: non-scaling-stroke;
1009
+ pointer-events: none;
1010
+ }
1011
+
1012
+ .be-canvas-selection-handle {
1013
+ fill: #fff;
1014
+ stroke: var(--noteloom-accent);
1015
+ stroke-width: 1.5;
1016
+ vector-effect: non-scaling-stroke;
1017
+ pointer-events: none;
1018
+ }
1019
+
1020
+ .be-canvas-rotate-handle {
1021
+ fill: var(--noteloom-accent);
1022
+ stroke: #fff;
1023
+ stroke-width: 1.5;
1024
+ vector-effect: non-scaling-stroke;
1025
+ pointer-events: none;
1026
+ }
1027
+
1028
+ .be-canvas-marquee {
1029
+ fill: color-mix(in srgb, var(--noteloom-accent) 12%, transparent);
1030
+ stroke: var(--noteloom-accent);
1031
+ stroke-width: 1.5;
1032
+ stroke-dasharray: 6 4;
1033
+ vector-effect: non-scaling-stroke;
1034
+ pointer-events: none;
1035
+ }
1036
+
1037
+ .be-canvas-grid-background {
1038
+ pointer-events: none;
1039
+ }
1040
+
1041
+ .be-canvas-grid-dot {
1042
+ fill: rgba(255, 255, 255, 0.18);
1043
+ }
1044
+
1045
+ .be-canvas-text-editor {
1046
+ width: 100%;
1047
+ height: 100%;
1048
+ margin: 0;
1049
+ padding: 0;
1050
+ border: 1px dashed var(--noteloom-accent);
1051
+ outline: none;
1052
+ background: transparent;
1053
+ resize: none;
1054
+ overflow: hidden;
1055
+ line-height: 1.3;
1056
+ font-family: inherit;
1057
+ white-space: pre-wrap;
1058
+ word-break: break-word;
1059
+ }
1060
+
1061
+ .be-canvas-resize-handle {
1062
+ position: absolute;
1063
+ bottom: 4px;
1064
+ right: 4px;
1065
+ width: 12px;
1066
+ height: 12px;
1067
+ cursor: nwse-resize;
1068
+ border-radius: 2px;
1069
+ border: 1px solid #fff;
1070
+ background: var(--noteloom-accent);
1071
+ }
1072
+
756
1073
  /* --- table --- */
757
1074
 
758
1075
  .be-table-scroll {
@@ -1878,8 +2195,8 @@ tr.be-table-row:hover .be-table-delete-row {
1878
2195
  }
1879
2196
 
1880
2197
  /* A mention chip always uses this one fixed color, not each option's own
1881
- tag color — matches Notion/Slack/GitHub's "@mentions are one consistent
1882
- color" convention rather than a per-person palette. */
2198
+ tag color — "@mentions are one consistent color" is the common
2199
+ convention rather than a per-person palette. */
1883
2200
  .be-select-tag-mention {
1884
2201
  background: var(--noteloom-accent-soft);
1885
2202
  color: var(--noteloom-accent);
@@ -2153,6 +2470,8 @@ tr.be-table-row:hover .be-table-delete-row {
2153
2470
  .be-embed-dropzone,
2154
2471
  .be-embed-upload-btn,
2155
2472
  .be-embed-url-row,
2473
+ .be-canvas-toolbar,
2474
+ .be-canvas-resize-handle,
2156
2475
  .be-callout-color-wrap,
2157
2476
  .be-modal-overlay,
2158
2477
  .be-mobile-action-bar,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "noteloom",
3
- "version": "0.1.6",
4
- "description": "Zero-runtime-dependency, React-first block/rich-text editor — Notion-style blocks, inline widgets, slash commands, undo/redo, and clipboard, with an optional default theme (noteloom/style.css).",
3
+ "version": "0.2.0",
4
+ "description": "Zero-runtime-dependency, React-first block/rich-text editor — nestable content blocks, inline widgets, slash commands, undo/redo, clipboard, real-time collaborative editing (a custom CRDT over WebRTC), offline-first persistence (IndexedDB + PWA-ready), and live presence, with an optional default theme (noteloom/style.css).",
5
5
  "keywords": [
6
6
  "react",
7
7
  "editor",
@@ -9,8 +9,13 @@
9
9
  "block-editor",
10
10
  "wysiwyg",
11
11
  "contenteditable",
12
- "notion",
13
- "slash-commands"
12
+ "slash-commands",
13
+ "collaborative-editing",
14
+ "crdt",
15
+ "webrtc",
16
+ "offline-first",
17
+ "pwa",
18
+ "real-time"
14
19
  ],
15
20
  "license": "MIT",
16
21
  "author": "Nikhil Vishwakarma",
@@ -28,9 +33,11 @@
28
33
  ],
29
34
  "main": "./dist/noteloom.cjs",
30
35
  "module": "./dist/noteloom.es.js",
36
+ "types": "./dist/index.d.ts",
31
37
  "style": "./dist/style.css",
32
38
  "exports": {
33
39
  ".": {
40
+ "types": "./dist/index.d.ts",
34
41
  "import": "./dist/noteloom.es.js",
35
42
  "require": "./dist/noteloom.cjs",
36
43
  "default": "./dist/noteloom.es.js"
@@ -46,9 +53,17 @@
46
53
  },
47
54
  "scripts": {
48
55
  "dev": "vite --config examples/basic/vite.config.js",
49
- "build": "vite build && node -e \"require('fs').copyFileSync('src/style.css','dist/style.css')\"",
56
+ "dev:quickstart": "vite --config examples/01-quickstart/vite.config.js",
57
+ "dev:custom-block": "vite --config examples/02-custom-block/vite.config.js",
58
+ "dev:custom-field-type": "vite --config examples/03-custom-field-type/vite.config.js",
59
+ "dev:styling": "vite --config examples/04-styling/vite.config.js",
60
+ "dev:collab": "vite --config examples/collab/vite.config.js",
61
+ "dev:lan-collab": "vite --config examples/lan-collab/vite.config.js",
62
+ "dev:offline-persist": "vite --config examples/offline-persist/vite.config.js",
63
+ "build": "vite build && node -e \"require('fs').copyFileSync('src/style.css','dist/style.css');require('fs').copyFileSync('src/index.d.ts','dist/index.d.ts')\"",
50
64
  "test": "vitest run",
51
- "prepublishOnly": "npm test && npm run build"
65
+ "typecheck": "tsc",
66
+ "prepublishOnly": "npm test && npm run typecheck && npm run build"
52
67
  },
53
68
  "peerDependencies": {
54
69
  "react": "^18.2.0 || ^19.0.0",
@@ -57,11 +72,16 @@
57
72
  "devDependencies": {
58
73
  "@testing-library/jest-dom": "^6.4.0",
59
74
  "@testing-library/react": "^16.0.0",
75
+ "@types/react": "^18.3.0",
76
+ "@types/react-dom": "^18.3.0",
60
77
  "@vitejs/plugin-react": "^4.3.0",
78
+ "fake-indexeddb": "^6.2.5",
61
79
  "jsdom": "^25.0.0",
62
80
  "react": "^18.3.1",
63
81
  "react-dom": "^18.3.1",
82
+ "typescript": "^5.6.0",
64
83
  "vite": "^5.4.0",
84
+ "vite-plugin-pwa": "^1.3.0",
65
85
  "vitest": "^2.1.0"
66
86
  }
67
87
  }