@filipc77/cowrite 0.4.13 → 0.4.14

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": "@filipc77/cowrite",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "description": "Live commenting plugin for coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {
package/ui/client.js CHANGED
@@ -12,6 +12,7 @@ const statusEl = $("#status");
12
12
  const popup = $("#commentPopup");
13
13
  const popupSelection = $("#popupSelection");
14
14
  const commentInput = $("#commentInput");
15
+ const commentTrigger = $("#commentTrigger");
15
16
  const filePicker = $("#filePicker");
16
17
  const fileList = $("#fileList");
17
18
 
@@ -120,38 +121,55 @@ function send(msg) {
120
121
  // --- Selection & Comment Creation ---
121
122
 
122
123
  document.addEventListener("mouseup", (e) => {
123
- // Don't trigger when clicking inside popup or sidebar
124
- if (popup.contains(e.target) || $("#sidebar").contains(e.target)) return;
124
+ // Don't trigger when clicking inside popup, sidebar, or trigger button
125
+ if (popup.contains(e.target) || commentTrigger.contains(e.target) || $("#sidebar").contains(e.target)) return;
125
126
 
126
127
  const selection = window.getSelection();
127
128
  if (!selection || selection.isCollapsed) {
128
- hidePopup();
129
+ hideTrigger();
129
130
  return;
130
131
  }
131
132
 
132
133
  const text = selection.toString().trim();
133
134
  if (!text) {
134
- hidePopup();
135
+ hideTrigger();
135
136
  return;
136
137
  }
137
138
 
138
139
  // Compute character offset in the source content
139
140
  const offset = computeOffset(selection, text);
140
141
  if (offset === -1) {
141
- hidePopup();
142
+ hideTrigger();
142
143
  return;
143
144
  }
144
145
 
145
146
  selectionInfo = { offset, length: text.length, selectedText: text };
146
147
 
147
- // Position popup near the selection
148
+ // Show the small "Comment" trigger button near the selection end
148
149
  const range = selection.getRangeAt(0);
149
150
  const rect = range.getBoundingClientRect();
150
- popup.style.left = `${Math.min(rect.left, window.innerWidth - 340)}px`;
151
- popup.style.top = `${rect.bottom + 8}px`;
152
- popupSelection.textContent = text;
151
+ commentTrigger.style.left = `${Math.min(rect.right + 8, window.innerWidth - 100)}px`;
152
+ commentTrigger.style.top = `${rect.top - 4}px`;
153
+ commentTrigger.hidden = false;
154
+ });
155
+
156
+ // Clicking the trigger button opens the full comment popup
157
+ commentTrigger.addEventListener("mousedown", (e) => {
158
+ // Prevent the mousedown from clearing the text selection
159
+ e.preventDefault();
160
+ });
161
+
162
+ commentTrigger.addEventListener("click", () => {
163
+ if (!selectionInfo) return;
164
+
165
+ // Position the popup near the trigger
166
+ const triggerRect = commentTrigger.getBoundingClientRect();
167
+ popup.style.left = `${Math.min(triggerRect.left, window.innerWidth - 340)}px`;
168
+ popup.style.top = `${triggerRect.bottom + 8}px`;
169
+ popupSelection.textContent = selectionInfo.selectedText;
153
170
  commentInput.value = "";
154
171
  popup.hidden = false;
172
+ commentTrigger.hidden = true;
155
173
  commentInput.focus();
156
174
  });
157
175
 
@@ -247,8 +265,17 @@ function computeDomOffset(node, charOffset) {
247
265
  return -1;
248
266
  }
249
267
 
268
+ function hideTrigger() {
269
+ commentTrigger.hidden = true;
270
+ // Only clear selectionInfo if popup isn't open
271
+ if (popup.hidden) {
272
+ selectionInfo = null;
273
+ }
274
+ }
275
+
250
276
  function hidePopup() {
251
277
  popup.hidden = true;
278
+ commentTrigger.hidden = true;
252
279
  selectionInfo = null;
253
280
  }
254
281
 
@@ -478,5 +505,16 @@ themeToggle.addEventListener("change", () => {
478
505
  applyTheme(theme);
479
506
  });
480
507
 
508
+ // Hide trigger when selection is cleared (e.g. clicking elsewhere)
509
+ document.addEventListener("selectionchange", () => {
510
+ const selection = window.getSelection();
511
+ if (!selection || selection.isCollapsed) {
512
+ // Small delay to avoid race with the trigger button click
513
+ setTimeout(() => {
514
+ if (popup.hidden) hideTrigger();
515
+ }, 100);
516
+ }
517
+ });
518
+
481
519
  // --- Init ---
482
520
  connect();
package/ui/index.html CHANGED
@@ -50,7 +50,10 @@
50
50
  </div>
51
51
  </main>
52
52
 
53
- <!-- Floating comment button/form -->
53
+ <!-- Floating comment button (appears on text selection) -->
54
+ <button class="comment-trigger" id="commentTrigger" hidden>Comment</button>
55
+
56
+ <!-- Comment form (appears when comment button is clicked) -->
54
57
  <div class="comment-popup" id="commentPopup" hidden>
55
58
  <div class="popup-header">Selection</div>
56
59
  <div class="popup-selection" id="popupSelection"></div>
package/ui/styles.css CHANGED
@@ -671,6 +671,34 @@ main {
671
671
  transform: scale(0.97);
672
672
  }
673
673
 
674
+ /* ---- Comment trigger button ---- */
675
+ .comment-trigger {
676
+ position: fixed;
677
+ z-index: 999;
678
+ font-family: var(--font-sans);
679
+ font-size: 12px;
680
+ font-weight: 600;
681
+ padding: 6px 14px;
682
+ border-radius: var(--radius-sm);
683
+ border: 1px solid var(--border);
684
+ background: var(--surface);
685
+ color: var(--accent);
686
+ cursor: pointer;
687
+ box-shadow: var(--shadow-md);
688
+ transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
689
+ animation: popup-enter 0.15s ease;
690
+ }
691
+
692
+ .comment-trigger:hover {
693
+ background: var(--accent);
694
+ color: var(--bg);
695
+ border-color: var(--accent);
696
+ }
697
+
698
+ .comment-trigger:active {
699
+ transform: scale(0.96);
700
+ }
701
+
674
702
  /* ---- Comment popup ---- */
675
703
  .comment-popup {
676
704
  position: fixed;