noteloom 0.1.7 → 0.3.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,9 +101,35 @@
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
 
119
+ /* A run with marks.commentIds set (see src/comments/commentMarks.js) --
120
+ presence/absence only, no per-comment value to vary the color by.
121
+ cursor: pointer hints that it opens CommentPopover on click. */
122
+ .be-comment-highlight {
123
+ background: rgba(234, 179, 8, 0.2);
124
+ border-bottom: 2px solid rgba(234, 179, 8, 0.6);
125
+ cursor: pointer;
126
+ }
127
+
128
+ /* Echoes a hover from the panel/popover back onto the actual text -- see useHoveredComment. */
129
+ .be-comment-highlight-active {
130
+ background: rgba(234, 179, 8, 0.4);
131
+ }
132
+
107
133
  /* --- basic leaf blocks --- */
108
134
 
109
135
  .be-paragraph {
@@ -797,7 +823,7 @@
797
823
  .be-canvas-toolbar-btn {
798
824
  border: none;
799
825
  background: transparent;
800
- width: 26px;
826
+ width: 28px;
801
827
  height: 24px;
802
828
  display: flex;
803
829
  align-items: center;
@@ -1691,6 +1717,67 @@ tr.be-table-row:hover .be-table-delete-row {
1691
1717
  color: var(--noteloom-danger);
1692
1718
  }
1693
1719
 
1720
+ /* --- template picker (see TemplatePicker.jsx) --- */
1721
+
1722
+ .be-template-picker {
1723
+ list-style: none;
1724
+ margin: 0;
1725
+ padding: 0;
1726
+ display: flex;
1727
+ flex-direction: column;
1728
+ gap: 6px;
1729
+ }
1730
+
1731
+ .be-template-picker-empty {
1732
+ color: var(--noteloom-text-faint);
1733
+ font-size: 0.88rem;
1734
+ }
1735
+
1736
+ .be-template-card {
1737
+ display: flex;
1738
+ align-items: center;
1739
+ justify-content: space-between;
1740
+ gap: 12px;
1741
+ padding: 10px 12px;
1742
+ border: 1px solid var(--noteloom-border);
1743
+ border-radius: var(--noteloom-radius-md);
1744
+ background: var(--noteloom-bg);
1745
+ }
1746
+
1747
+ .be-template-card-body {
1748
+ display: flex;
1749
+ flex-direction: column;
1750
+ gap: 2px;
1751
+ min-width: 0;
1752
+ }
1753
+
1754
+ .be-template-card-name {
1755
+ font-weight: 600;
1756
+ font-size: 0.92rem;
1757
+ color: var(--noteloom-text);
1758
+ }
1759
+
1760
+ .be-template-card-description {
1761
+ font-size: 0.82rem;
1762
+ color: var(--noteloom-text-muted);
1763
+ }
1764
+
1765
+ .be-template-card-select {
1766
+ flex: none;
1767
+ border: 1px solid var(--noteloom-border);
1768
+ background: var(--noteloom-bg-subtle);
1769
+ color: var(--noteloom-text);
1770
+ border-radius: var(--noteloom-radius-sm);
1771
+ padding: 5px 12px;
1772
+ font-size: 0.85rem;
1773
+ font-weight: 500;
1774
+ cursor: pointer;
1775
+ }
1776
+
1777
+ .be-template-card-select:hover {
1778
+ background: var(--noteloom-bg-hover);
1779
+ }
1780
+
1694
1781
  /* --- mobile action bar (pinned above the on-screen keyboard) --- */
1695
1782
 
1696
1783
  .be-mobile-action-bar {
@@ -2183,8 +2270,8 @@ tr.be-table-row:hover .be-table-delete-row {
2183
2270
  }
2184
2271
 
2185
2272
  /* A mention chip always uses this one fixed color, not each option's own
2186
- tag color — matches Notion/Slack/GitHub's "@mentions are one consistent
2187
- color" convention rather than a per-person palette. */
2273
+ tag color — "@mentions are one consistent color" is the common
2274
+ convention rather than a per-person palette. */
2188
2275
  .be-select-tag-mention {
2189
2276
  background: var(--noteloom-accent-soft);
2190
2277
  color: var(--noteloom-accent);
@@ -2363,6 +2450,323 @@ tr.be-table-row:hover .be-table-delete-row {
2363
2450
  color: var(--noteloom-text);
2364
2451
  }
2365
2452
 
2453
+ /* --- comments: avatar, header, composer, thread card, click/hover popover, right-side panel --- */
2454
+
2455
+ .be-comment-avatar {
2456
+ flex: none;
2457
+ display: flex;
2458
+ align-items: center;
2459
+ justify-content: center;
2460
+ border-radius: 50%;
2461
+ color: #fff;
2462
+ font-weight: 700;
2463
+ line-height: 1;
2464
+ }
2465
+
2466
+ /* Shared by CommentPopover and FloatingToolbar's standalone composer (see
2467
+ CommentPopoverHeader.jsx) -- title on the left, an explicit close button
2468
+ on the right so dismissing doesn't rely solely on Escape/outside-click. */
2469
+ .be-comment-popover-header {
2470
+ display: flex;
2471
+ align-items: center;
2472
+ justify-content: space-between;
2473
+ gap: 8px;
2474
+ margin-bottom: 10px;
2475
+ padding-bottom: 10px;
2476
+ border-bottom: 1px solid var(--noteloom-border);
2477
+ }
2478
+
2479
+ .be-comment-popover-title {
2480
+ font-weight: 700;
2481
+ font-size: 0.9rem;
2482
+ }
2483
+
2484
+ .be-comment-popover-close {
2485
+ flex: none;
2486
+ border: none;
2487
+ background: transparent;
2488
+ color: var(--noteloom-text-muted);
2489
+ width: 22px;
2490
+ height: 22px;
2491
+ display: flex;
2492
+ align-items: center;
2493
+ justify-content: center;
2494
+ border-radius: var(--noteloom-radius-sm);
2495
+ cursor: pointer;
2496
+ }
2497
+
2498
+ .be-comment-popover-close:hover {
2499
+ background: var(--noteloom-bg-hover);
2500
+ color: var(--noteloom-text);
2501
+ }
2502
+
2503
+ /* Overrides the dark `.be-floating-toolbar` chrome it also carries (see
2504
+ FloatingToolbar.jsx's own comment on why that base class stays) -- a
2505
+ comment composer reads better as a light card than white text on the
2506
+ toolbar's own near-black background. */
2507
+ .be-floating-toolbar.be-floating-toolbar-comment-standalone {
2508
+ display: block;
2509
+ background: var(--noteloom-bg);
2510
+ border: 1px solid var(--noteloom-border);
2511
+ padding: 10px 12px 12px;
2512
+ width: 300px;
2513
+ }
2514
+
2515
+ .be-comment-composer {
2516
+ display: flex;
2517
+ flex-direction: column;
2518
+ gap: 10px;
2519
+ margin-top: 10px;
2520
+ }
2521
+
2522
+ .be-comment-composer-input-row {
2523
+ display: flex;
2524
+ align-items: flex-start;
2525
+ gap: 8px;
2526
+ }
2527
+
2528
+ .be-comment-composer-textarea {
2529
+ flex: 1;
2530
+ min-width: 0;
2531
+ box-sizing: border-box;
2532
+ resize: none;
2533
+ /* ~2 lines to start, grows with content (see CommentComposer's own
2534
+ resizeToContent) up to ~5 lines, then scrolls. */
2535
+ min-height: 2.8em;
2536
+ max-height: 7.2em;
2537
+ overflow-y: auto;
2538
+ padding: 8px 10px;
2539
+ font: inherit;
2540
+ font-size: 0.85rem;
2541
+ line-height: 1.4;
2542
+ color: var(--noteloom-text);
2543
+ background: var(--noteloom-bg-subtle);
2544
+ border: 1px solid var(--noteloom-border);
2545
+ border-radius: var(--noteloom-radius-md);
2546
+ }
2547
+
2548
+ .be-comment-composer-textarea:focus {
2549
+ outline: none;
2550
+ border-color: var(--noteloom-accent);
2551
+ }
2552
+
2553
+ .be-comment-composer-actions {
2554
+ display: flex;
2555
+ justify-content: flex-end;
2556
+ gap: 8px;
2557
+ }
2558
+
2559
+ .be-comment-composer-cancel,
2560
+ .be-comment-composer-submit {
2561
+ display: flex;
2562
+ align-items: center;
2563
+ gap: 6px;
2564
+ border: none;
2565
+ border-radius: var(--noteloom-radius-md);
2566
+ padding: 6px 12px;
2567
+ font-size: 0.8rem;
2568
+ font-weight: 600;
2569
+ cursor: pointer;
2570
+ }
2571
+
2572
+ .be-comment-composer-cancel {
2573
+ background: var(--noteloom-bg-hover);
2574
+ color: var(--noteloom-text-muted);
2575
+ }
2576
+
2577
+ .be-comment-composer-submit {
2578
+ background: var(--noteloom-accent);
2579
+ color: #fff;
2580
+ }
2581
+
2582
+ .be-comment-composer-submit:disabled {
2583
+ opacity: 0.4;
2584
+ cursor: not-allowed;
2585
+ }
2586
+
2587
+ .be-comment-thread {
2588
+ font-size: 0.85rem;
2589
+ }
2590
+
2591
+ .be-comment-thread + .be-comment-thread {
2592
+ margin-top: 16px;
2593
+ padding-top: 16px;
2594
+ border-top: 1px solid var(--noteloom-border);
2595
+ }
2596
+
2597
+ .be-comment-thread-resolved {
2598
+ opacity: 0.6;
2599
+ }
2600
+
2601
+ /* Echoes a hover from the highlighted text back onto this card -- see useHoveredComment. */
2602
+ .be-comment-thread-hovered {
2603
+ background: var(--noteloom-bg-hover);
2604
+ border-radius: var(--noteloom-radius-md);
2605
+ margin: -6px;
2606
+ padding: 6px;
2607
+ }
2608
+
2609
+ .be-comment-message {
2610
+ display: flex;
2611
+ align-items: flex-start;
2612
+ gap: 10px;
2613
+ }
2614
+
2615
+ .be-comment-message + .be-comment-message {
2616
+ margin-top: 10px;
2617
+ }
2618
+
2619
+ .be-comment-message-body {
2620
+ flex: 1;
2621
+ min-width: 0;
2622
+ }
2623
+
2624
+ .be-comment-message-header {
2625
+ display: flex;
2626
+ align-items: baseline;
2627
+ gap: 6px;
2628
+ }
2629
+
2630
+ .be-comment-message-author {
2631
+ font-weight: 600;
2632
+ font-size: 0.78rem;
2633
+ }
2634
+
2635
+ .be-comment-message-time {
2636
+ font-size: 0.72rem;
2637
+ color: var(--noteloom-text-muted);
2638
+ }
2639
+
2640
+ .be-comment-message-text {
2641
+ margin: 3px 0 0;
2642
+ line-height: 1.45;
2643
+ white-space: pre-wrap;
2644
+ word-break: break-word;
2645
+ }
2646
+
2647
+ /* Replies (every message after the thread's first) get a connecting
2648
+ vertical line + indent, chat-thread style -- our data model has no
2649
+ deeper nesting than "root message + flat replies", so one indent level
2650
+ is all this needs. */
2651
+ .be-comment-thread-replies {
2652
+ margin: 10px 0 0 13px;
2653
+ padding-left: 14px;
2654
+ border-left: 2px solid var(--noteloom-border);
2655
+ }
2656
+
2657
+ .be-comment-thread-actions {
2658
+ display: flex;
2659
+ align-items: center;
2660
+ gap: 4px;
2661
+ margin-top: 10px;
2662
+ opacity: 0;
2663
+ transition: opacity 0.1s ease;
2664
+ }
2665
+
2666
+ .be-comment-thread:hover .be-comment-thread-actions,
2667
+ .be-comment-thread:focus-within .be-comment-thread-actions {
2668
+ opacity: 1;
2669
+ }
2670
+
2671
+ .be-comment-thread-actions button {
2672
+ display: flex;
2673
+ align-items: center;
2674
+ gap: 4px;
2675
+ border: none;
2676
+ background: transparent;
2677
+ color: var(--noteloom-text-muted);
2678
+ font-size: 0.78rem;
2679
+ padding: 4px 8px;
2680
+ border-radius: var(--noteloom-radius-sm);
2681
+ cursor: pointer;
2682
+ }
2683
+
2684
+ .be-comment-thread-actions button:hover {
2685
+ background: var(--noteloom-bg-hover);
2686
+ color: var(--noteloom-text);
2687
+ }
2688
+
2689
+ .be-comment-thread-delete {
2690
+ margin-left: auto;
2691
+ }
2692
+
2693
+ /* --- comments: click/hover popover over existing highlighted text --- */
2694
+
2695
+ .be-comment-popover {
2696
+ z-index: 1000;
2697
+ width: 300px;
2698
+ max-width: calc(100vw - 24px);
2699
+ background: var(--noteloom-bg);
2700
+ border: 1px solid var(--noteloom-border);
2701
+ border-radius: var(--noteloom-radius-lg);
2702
+ padding: 12px 14px 14px;
2703
+ box-shadow: var(--noteloom-shadow);
2704
+ }
2705
+
2706
+ /* --- comments: opt-in right-side panel (NoteloomEditor showCommentsPanel) --- */
2707
+
2708
+ /* Deliberately no border/shadow/box background here, or on
2709
+ .be-comments-panel-item below -- comments float directly on the page
2710
+ like margin notes, not a boxed widget, matching how most other editors'
2711
+ own comment sidebars read. */
2712
+ /* No overflow/scrolling of its own on purpose -- each card's position
2713
+ already tracks the PAGE's scroll (see CommentsPanel's recompute()), so
2714
+ the panel scrolling independently would fight that: a wheel gesture over
2715
+ it needs to fall through and scroll the page, not the panel's own box,
2716
+ or cards visually detach from the text they're anchored to. Cards whose
2717
+ anchor has scrolled off-screen just go negative/beyond the viewport and
2718
+ aren't painted -- no clipping box needed for that. */
2719
+ .be-comments-panel {
2720
+ position: fixed;
2721
+ top: 80px;
2722
+ right: 24px;
2723
+ width: 320px;
2724
+ overflow: visible;
2725
+ background: transparent;
2726
+ padding: 0 12px;
2727
+ font-size: 0.85rem;
2728
+ z-index: 500;
2729
+ pointer-events: none;
2730
+ }
2731
+
2732
+ .be-comments-panel-item,
2733
+ .be-comments-panel-empty {
2734
+ pointer-events: auto;
2735
+ }
2736
+
2737
+ .be-comments-panel-empty {
2738
+ color: var(--noteloom-text-muted);
2739
+ font-size: 0.82rem;
2740
+ margin: 0;
2741
+ }
2742
+
2743
+ /* Each .be-comments-panel-item is absolutely positioned (top set inline by
2744
+ CommentsPanel, aligned with its thread's own anchored text, margin-comment
2745
+ style) inside this relatively-positioned container -- height is also set
2746
+ inline, sized to fit the lowest card. */
2747
+ .be-comments-panel-list {
2748
+ position: relative;
2749
+ }
2750
+
2751
+ .be-comments-panel-item {
2752
+ position: absolute;
2753
+ left: 0;
2754
+ right: 0;
2755
+ padding: 8px 12px;
2756
+ border-radius: var(--noteloom-radius-md);
2757
+ transition: top 0.15s ease, background-color 0.1s ease;
2758
+ }
2759
+
2760
+ .be-comments-panel-item:hover {
2761
+ background: var(--noteloom-bg-hover);
2762
+ }
2763
+
2764
+ @media (max-width: 900px) {
2765
+ .be-comments-panel {
2766
+ display: none;
2767
+ }
2768
+ }
2769
+
2366
2770
  /* --- voice typing: caret-anchored listening/processing indicator --- */
2367
2771
 
2368
2772
  .be-voice-indicator {
@@ -2446,6 +2850,8 @@ tr.be-table-row:hover .be-table-delete-row {
2446
2850
  #be-at-menu,
2447
2851
  .be-floating-toolbar,
2448
2852
  .be-link-hover-card,
2853
+ .be-comment-popover,
2854
+ .be-comments-panel,
2449
2855
  .be-voice-indicator,
2450
2856
  .be-table-header-menu-trigger,
2451
2857
  .be-table-header-menu,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "noteloom",
3
- "version": "0.1.7",
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.3.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,22 +53,40 @@
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:templates": "vite --config examples/05-templates/vite.config.js",
61
+ "dev:comments": "vite --config examples/06-comments/vite.config.js",
62
+ "dev:version-history": "vite --config examples/07-version-history/vite.config.js",
63
+ "dev:collab": "vite --config examples/collab/vite.config.js",
64
+ "dev:lan-collab": "vite --config examples/lan-collab/vite.config.js",
65
+ "dev:offline-persist": "vite --config examples/offline-persist/vite.config.js",
66
+ "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
67
  "test": "vitest run",
51
- "prepublishOnly": "npm test && npm run build"
68
+ "test:e2e": "playwright test",
69
+ "typecheck": "tsc",
70
+ "prepublishOnly": "npm test && npm run typecheck && npm run build"
52
71
  },
53
72
  "peerDependencies": {
54
73
  "react": "^18.2.0 || ^19.0.0",
55
74
  "react-dom": "^18.2.0 || ^19.0.0"
56
75
  },
57
76
  "devDependencies": {
77
+ "@playwright/test": "^1.62.0",
58
78
  "@testing-library/jest-dom": "^6.4.0",
59
79
  "@testing-library/react": "^16.0.0",
80
+ "@types/react": "^18.3.0",
81
+ "@types/react-dom": "^18.3.0",
60
82
  "@vitejs/plugin-react": "^4.3.0",
83
+ "fake-indexeddb": "^6.2.5",
61
84
  "jsdom": "^25.0.0",
62
85
  "react": "^18.3.1",
63
86
  "react-dom": "^18.3.1",
87
+ "typescript": "^5.6.0",
64
88
  "vite": "^5.4.0",
89
+ "vite-plugin-pwa": "^1.3.0",
65
90
  "vitest": "^2.1.0"
66
91
  }
67
92
  }