@filipc77/cowrite 0.6.4 → 0.6.6
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/README.md +15 -4
- package/dist/bin/cowrite.js +114 -3
- package/dist/bin/cowrite.js.map +1 -1
- package/package.json +1 -1
- package/ui/client.js +201 -94
- package/ui/index.html +6 -7
- package/ui/styles.css +244 -5
package/ui/client.js
CHANGED
|
@@ -14,9 +14,6 @@ const popupSelection = $("#popupSelection");
|
|
|
14
14
|
const commentInput = $("#commentInput");
|
|
15
15
|
const selectionToolbar = $("#selectionToolbar");
|
|
16
16
|
const commentTrigger = $("#commentTrigger");
|
|
17
|
-
const highlightPopover = $("#highlightPopover");
|
|
18
|
-
const highlightPopoverText = $("#highlightPopoverText");
|
|
19
|
-
const highlightEditBtn = $("#highlightEditBtn");
|
|
20
17
|
const undoBtn = $("#undoBtn");
|
|
21
18
|
const filePicker = $("#filePicker");
|
|
22
19
|
const fileList = $("#fileList");
|
|
@@ -34,7 +31,6 @@ let currentBlocks = [];
|
|
|
34
31
|
let insertBtn = null;
|
|
35
32
|
let insertLine = null;
|
|
36
33
|
let activeGapIndex = -1;
|
|
37
|
-
let activeHighlightCommentId = null;
|
|
38
34
|
let undoStack = [];
|
|
39
35
|
const MAX_UNDO = 50;
|
|
40
36
|
|
|
@@ -47,6 +43,43 @@ let pendingFileUpdate = null;
|
|
|
47
43
|
let pendingEditAfterInsert = -1;
|
|
48
44
|
let contentEditableActive = false;
|
|
49
45
|
|
|
46
|
+
// --- Resizable Sidebar ---
|
|
47
|
+
(function initResizableSidebar() {
|
|
48
|
+
const handle = document.getElementById("sidebarDragHandle");
|
|
49
|
+
const sidebar = document.getElementById("sidebar");
|
|
50
|
+
if (!handle || !sidebar) return;
|
|
51
|
+
|
|
52
|
+
// Restore saved width
|
|
53
|
+
const saved = localStorage.getItem("cowrite-sidebar-width");
|
|
54
|
+
if (saved) document.documentElement.style.setProperty("--sidebar-width", saved + "px");
|
|
55
|
+
|
|
56
|
+
let startX = 0;
|
|
57
|
+
let startWidth = 0;
|
|
58
|
+
|
|
59
|
+
handle.addEventListener("mousedown", (e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
startX = e.clientX;
|
|
62
|
+
startWidth = sidebar.offsetWidth;
|
|
63
|
+
document.body.classList.add("sidebar-resizing");
|
|
64
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
65
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function onMouseMove(e) {
|
|
69
|
+
const delta = startX - e.clientX; // sidebar is on the right
|
|
70
|
+
const newWidth = Math.min(Math.max(startWidth + delta, 300), window.innerWidth * 0.5);
|
|
71
|
+
document.documentElement.style.setProperty("--sidebar-width", newWidth + "px");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function onMouseUp() {
|
|
75
|
+
document.body.classList.remove("sidebar-resizing");
|
|
76
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
77
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
78
|
+
const width = sidebar.offsetWidth;
|
|
79
|
+
localStorage.setItem("cowrite-sidebar-width", String(width));
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
|
|
50
83
|
const BLOCK_TYPES = [
|
|
51
84
|
{ id: "text", label: "Text", category: "Basic blocks", icon: "Aa", template: "\u200B" },
|
|
52
85
|
{ id: "h1", label: "Heading 1", category: "Basic blocks", icon: "H1", template: "# " },
|
|
@@ -91,15 +124,36 @@ function switchFile(file) {
|
|
|
91
124
|
history.replaceState(null, "", url.toString());
|
|
92
125
|
}
|
|
93
126
|
|
|
127
|
+
// Track meta key for Cmd+Click to open in new tab
|
|
128
|
+
let lastClickHadMeta = false;
|
|
129
|
+
document.addEventListener("mousedown", (e) => { lastClickHadMeta = e.metaKey || e.ctrlKey; });
|
|
130
|
+
|
|
131
|
+
function openFileInNewTab(file) {
|
|
132
|
+
const url = new URL(location.href);
|
|
133
|
+
url.searchParams.set("file", file);
|
|
134
|
+
window.open(url.toString(), "_blank");
|
|
135
|
+
}
|
|
136
|
+
|
|
94
137
|
filePicker.addEventListener("change", () => {
|
|
95
138
|
const file = filePicker.value.trim();
|
|
96
|
-
if (file)
|
|
139
|
+
if (!file) return;
|
|
140
|
+
if (lastClickHadMeta) {
|
|
141
|
+
openFileInNewTab(file);
|
|
142
|
+
filePicker.value = "";
|
|
143
|
+
} else {
|
|
144
|
+
switchFile(file);
|
|
145
|
+
}
|
|
97
146
|
});
|
|
98
147
|
|
|
99
148
|
filePicker.addEventListener("keydown", (e) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
const file = filePicker.value.trim();
|
|
150
|
+
if (!file) return;
|
|
151
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
openFileInNewTab(file);
|
|
154
|
+
filePicker.value = "";
|
|
155
|
+
} else if (e.key === "Enter") {
|
|
156
|
+
switchFile(file);
|
|
103
157
|
}
|
|
104
158
|
});
|
|
105
159
|
|
|
@@ -526,48 +580,99 @@ function renderComments() {
|
|
|
526
580
|
return;
|
|
527
581
|
}
|
|
528
582
|
|
|
529
|
-
commentListEl.innerHTML = comments.map((c) =>
|
|
530
|
-
|
|
531
|
-
<
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
583
|
+
commentListEl.innerHTML = comments.map((c) => {
|
|
584
|
+
const repliesHtml = c.replies.length > 0 ? `
|
|
585
|
+
<div class="comment-replies">
|
|
586
|
+
${c.replies.map((r) => r.proposal ? `
|
|
587
|
+
<div class="reply agent proposal-reply proposal-${r.proposal.status}">
|
|
588
|
+
<div class="reply-from agent">agent — proposal</div>
|
|
589
|
+
<div class="proposal-explanation">${escapeHtml(r.proposal.explanation)}</div>
|
|
590
|
+
${r.proposal.status === "pending" ? `
|
|
591
|
+
<div class="proposal-diff">
|
|
592
|
+
<div class="proposal-old"><span class="proposal-label">Current</span><pre>${escapeHtml(r.proposal.oldText)}</pre></div>
|
|
593
|
+
<div class="proposal-new"><span class="proposal-label">Proposed</span><pre>${escapeHtml(r.proposal.newText)}</pre></div>
|
|
594
|
+
</div>
|
|
595
|
+
<div class="proposal-actions">
|
|
596
|
+
<button class="proposal-apply-btn" onclick="applyProposal('${c.id}', '${r.id}')">Apply</button>
|
|
597
|
+
<button class="proposal-reject-btn" onclick="rejectProposal('${c.id}', '${r.id}')">Reject</button>
|
|
598
|
+
</div>
|
|
599
|
+
` : r.proposal.status === "applied" ? `
|
|
600
|
+
<div class="proposal-diff">
|
|
601
|
+
<div class="proposal-new"><span class="proposal-label">✓ Applied</span><pre>${escapeHtml(r.proposal.newText)}</pre></div>
|
|
602
|
+
</div>
|
|
603
|
+
` : `
|
|
604
|
+
<div class="proposal-diff">
|
|
605
|
+
<div class="proposal-old"><span class="proposal-label">✗ Rejected</span><pre>${escapeHtml(r.proposal.oldText)}</pre></div>
|
|
606
|
+
</div>
|
|
607
|
+
`}
|
|
608
|
+
</div>
|
|
609
|
+
` : `
|
|
610
|
+
<div class="reply ${r.from}">
|
|
611
|
+
<div class="reply-from ${r.from}">${r.from}</div>
|
|
612
|
+
<div>${escapeHtml(r.text)}</div>
|
|
613
|
+
</div>
|
|
614
|
+
`).join("")}
|
|
615
|
+
</div>
|
|
616
|
+
` : "";
|
|
617
|
+
|
|
618
|
+
if (c.status === "resolved") {
|
|
619
|
+
const truncated = c.comment.length > 60 ? c.comment.slice(0, 60) + "..." : c.comment;
|
|
620
|
+
return `
|
|
621
|
+
<div class="comment-card resolved" data-id="${c.id}">
|
|
622
|
+
<button class="comment-delete-btn" onclick="deleteComment('${c.id}')" title="Delete comment">
|
|
623
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
|
624
|
+
</button>
|
|
625
|
+
<div class="resolved-summary" onclick="toggleResolvedExpand('${c.id}')">
|
|
626
|
+
<svg class="resolved-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
|
627
|
+
<span class="comment-status resolved">resolved</span>
|
|
628
|
+
<span class="resolved-summary-text">${escapeHtml(truncated)}</span>
|
|
629
|
+
</div>
|
|
630
|
+
<div class="resolved-details" hidden>
|
|
631
|
+
${c.selectedText
|
|
632
|
+
? `<div class="comment-selected-text">${escapeHtml(c.selectedText)}</div>`
|
|
633
|
+
: `<div class="comment-file-badge">Whole file</div>`
|
|
634
|
+
}
|
|
635
|
+
<div class="comment-text">${escapeHtml(c.comment)}</div>
|
|
636
|
+
${repliesHtml}
|
|
637
|
+
<div class="comment-meta">
|
|
638
|
+
<span>${timeAgo(c.createdAt)}</span>
|
|
545
639
|
</div>
|
|
546
|
-
|
|
640
|
+
<div class="comment-actions">
|
|
641
|
+
<button onclick="reopenComment('${c.id}')">Reopen</button>
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return `
|
|
649
|
+
<div class="comment-card ${c.status}" data-id="${c.id}">
|
|
650
|
+
<button class="comment-delete-btn" onclick="deleteComment('${c.id}')" title="Delete comment">
|
|
651
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
|
652
|
+
</button>
|
|
653
|
+
${c.selectedText
|
|
654
|
+
? `<div class="comment-selected-text">${escapeHtml(c.selectedText)}</div>`
|
|
655
|
+
: `<div class="comment-file-badge">Whole file</div>`
|
|
656
|
+
}
|
|
657
|
+
<div class="comment-text">${escapeHtml(c.comment)}</div>
|
|
658
|
+
${repliesHtml}
|
|
659
|
+
<div class="comment-meta">
|
|
660
|
+
<span>${timeAgo(c.createdAt)}</span>
|
|
661
|
+
<span class="comment-status ${c.status}">${c.status}</span>
|
|
547
662
|
</div>
|
|
548
|
-
` : ""}
|
|
549
|
-
<div class="comment-meta">
|
|
550
|
-
<span>${timeAgo(c.createdAt)}</span>
|
|
551
|
-
<span class="comment-status ${c.status}">${c.status}</span>
|
|
552
|
-
</div>
|
|
553
|
-
${c.status !== "resolved" ? `
|
|
554
663
|
<div class="comment-actions">
|
|
555
664
|
<button onclick="showReplyForm('${c.id}')">Reply</button>
|
|
556
665
|
<button onclick="resolveComment('${c.id}')">Resolve</button>
|
|
557
666
|
</div>
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
<div class="reply-form" id="reply-form-${c.id}" hidden>
|
|
564
|
-
<textarea rows="2" placeholder="Reply..."></textarea>
|
|
565
|
-
<div class="reply-form-actions">
|
|
566
|
-
<button onclick="submitReply('${c.id}')">Send</button>
|
|
667
|
+
<div class="reply-form" id="reply-form-${c.id}" hidden>
|
|
668
|
+
<textarea rows="2" placeholder="Reply..."></textarea>
|
|
669
|
+
<div class="reply-form-actions">
|
|
670
|
+
<button onclick="submitReply('${c.id}')">Send</button>
|
|
671
|
+
</div>
|
|
567
672
|
</div>
|
|
568
673
|
</div>
|
|
569
|
-
|
|
570
|
-
|
|
674
|
+
`;
|
|
675
|
+
}).join("");
|
|
571
676
|
|
|
572
677
|
// Click to scroll to highlight
|
|
573
678
|
for (const card of commentListEl.querySelectorAll(".comment-card")) {
|
|
@@ -615,6 +720,25 @@ window.submitReply = function (id) {
|
|
|
615
720
|
form.hidden = true;
|
|
616
721
|
};
|
|
617
722
|
|
|
723
|
+
window.applyProposal = function (commentId, replyId) {
|
|
724
|
+
send({ type: "proposal_apply", commentId, replyId });
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
window.rejectProposal = function (commentId, replyId) {
|
|
728
|
+
send({ type: "proposal_reject", commentId, replyId });
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
window.toggleResolvedExpand = function (id) {
|
|
732
|
+
const card = commentListEl.querySelector(`.comment-card[data-id="${id}"]`);
|
|
733
|
+
if (!card) return;
|
|
734
|
+
const details = card.querySelector(".resolved-details");
|
|
735
|
+
const chevron = card.querySelector(".resolved-chevron");
|
|
736
|
+
if (!details) return;
|
|
737
|
+
const expanding = details.hidden;
|
|
738
|
+
details.hidden = !expanding;
|
|
739
|
+
card.classList.toggle("resolved-expanded", expanding);
|
|
740
|
+
};
|
|
741
|
+
|
|
618
742
|
// --- Highlights ---
|
|
619
743
|
|
|
620
744
|
function applyHighlights() {
|
|
@@ -743,6 +867,24 @@ themeToggle.addEventListener("change", () => {
|
|
|
743
867
|
applyTheme(theme);
|
|
744
868
|
});
|
|
745
869
|
|
|
870
|
+
// --- Font size toggle ---
|
|
871
|
+
const FONT_SIZE_KEY = "cowrite-font-size";
|
|
872
|
+
(function initFontSize() {
|
|
873
|
+
const saved = localStorage.getItem(FONT_SIZE_KEY) || "large";
|
|
874
|
+
if (saved === "large") document.body.classList.add("font-large");
|
|
875
|
+
for (const btn of document.querySelectorAll(".font-size-btn")) {
|
|
876
|
+
btn.setAttribute("aria-pressed", btn.dataset.size === saved ? "true" : "false");
|
|
877
|
+
btn.addEventListener("click", () => {
|
|
878
|
+
const size = btn.dataset.size;
|
|
879
|
+
document.body.classList.toggle("font-large", size === "large");
|
|
880
|
+
localStorage.setItem(FONT_SIZE_KEY, size);
|
|
881
|
+
for (const b of document.querySelectorAll(".font-size-btn")) {
|
|
882
|
+
b.setAttribute("aria-pressed", b.dataset.size === size ? "true" : "false");
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
})();
|
|
887
|
+
|
|
746
888
|
// Hide trigger when selection is cleared
|
|
747
889
|
document.addEventListener("selectionchange", () => {
|
|
748
890
|
const selection = window.getSelection();
|
|
@@ -1490,15 +1632,23 @@ fileContentEl.addEventListener("click", (e) => {
|
|
|
1490
1632
|
const target = e.target;
|
|
1491
1633
|
if (target.closest("a, .mermaid-container, .block-insert-btn, .block-type-menu, .inline-editor, .block-edit-wrapper, .block-editing, .code-block-header")) return;
|
|
1492
1634
|
|
|
1493
|
-
// Handle comment highlight clicks
|
|
1635
|
+
// Handle comment highlight clicks — scroll to sidebar card (skip resolved, let them edit)
|
|
1494
1636
|
const highlightEl = target.closest(".comment-highlight");
|
|
1495
1637
|
if (highlightEl) {
|
|
1496
1638
|
const commentId = highlightEl.dataset.commentId;
|
|
1497
1639
|
const comment = comments.find(c => c.id === commentId);
|
|
1498
|
-
if (comment) {
|
|
1499
|
-
|
|
1640
|
+
if (comment && comment.status !== "resolved") {
|
|
1641
|
+
for (const card of commentListEl.querySelectorAll(".comment-card")) {
|
|
1642
|
+
card.classList.remove("active");
|
|
1643
|
+
}
|
|
1644
|
+
const card = commentListEl.querySelector(`.comment-card[data-id="${comment.id}"]`);
|
|
1645
|
+
if (card) {
|
|
1646
|
+
card.classList.add("active");
|
|
1647
|
+
card.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
1648
|
+
}
|
|
1649
|
+
return;
|
|
1500
1650
|
}
|
|
1501
|
-
|
|
1651
|
+
// Resolved highlights fall through to block editing
|
|
1502
1652
|
}
|
|
1503
1653
|
|
|
1504
1654
|
if (!currentBlocks.length) return;
|
|
@@ -1523,55 +1673,12 @@ fileContentEl.addEventListener("click", (e) => {
|
|
|
1523
1673
|
});
|
|
1524
1674
|
});
|
|
1525
1675
|
|
|
1526
|
-
// --- Highlight
|
|
1527
|
-
|
|
1528
|
-
function showHighlightPopover(el, comment) {
|
|
1529
|
-
activeHighlightCommentId = comment.id;
|
|
1530
|
-
highlightPopoverText.textContent = comment.comment;
|
|
1531
|
-
|
|
1532
|
-
const rect = el.getBoundingClientRect();
|
|
1533
|
-
highlightPopover.style.left = `${Math.min(rect.left, window.innerWidth - 260)}px`;
|
|
1534
|
-
highlightPopover.style.top = `${rect.bottom + 8}px`;
|
|
1535
|
-
highlightPopover.hidden = false;
|
|
1536
|
-
|
|
1537
|
-
// Highlight and scroll to the corresponding comment card in the sidebar
|
|
1538
|
-
for (const card of commentListEl.querySelectorAll(".comment-card")) {
|
|
1539
|
-
card.classList.remove("active");
|
|
1540
|
-
}
|
|
1541
|
-
const card = commentListEl.querySelector(`.comment-card[data-id="${comment.id}"]`);
|
|
1542
|
-
if (card) {
|
|
1543
|
-
card.classList.add("active");
|
|
1544
|
-
card.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
function hideHighlightPopover() {
|
|
1549
|
-
highlightPopover.hidden = true;
|
|
1550
|
-
activeHighlightCommentId = null;
|
|
1551
|
-
for (const card of commentListEl.querySelectorAll(".comment-card.active")) {
|
|
1552
|
-
card.classList.remove("active");
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
highlightEditBtn.addEventListener("click", () => {
|
|
1557
|
-
if (!activeHighlightCommentId) return;
|
|
1558
|
-
const comment = comments.find(c => c.id === activeHighlightCommentId);
|
|
1559
|
-
if (!comment) { hideHighlightPopover(); return; }
|
|
1560
|
-
|
|
1561
|
-
for (let i = 0; i < currentBlocks.length; i++) {
|
|
1562
|
-
const b = currentBlocks[i];
|
|
1563
|
-
if (comment.offset >= b.sourceStart && comment.offset < b.sourceEnd) {
|
|
1564
|
-
hideHighlightPopover();
|
|
1565
|
-
enterBlockEditDispatch(i);
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
hideHighlightPopover();
|
|
1570
|
-
});
|
|
1571
|
-
|
|
1676
|
+
// --- Highlight click: clear active on outside click ---
|
|
1572
1677
|
document.addEventListener("mousedown", (e) => {
|
|
1573
|
-
if (!
|
|
1574
|
-
|
|
1678
|
+
if (!e.target.closest(".comment-highlight")) {
|
|
1679
|
+
for (const card of commentListEl.querySelectorAll(".comment-card.active")) {
|
|
1680
|
+
card.classList.remove("active");
|
|
1681
|
+
}
|
|
1575
1682
|
}
|
|
1576
1683
|
});
|
|
1577
1684
|
|
package/ui/index.html
CHANGED
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
<span class="file-path" id="filePath"></span>
|
|
27
27
|
</div>
|
|
28
28
|
<div class="header-right">
|
|
29
|
+
<div class="font-size-toggle" id="fontSizeToggle">
|
|
30
|
+
<button class="font-size-btn" data-size="regular" title="Regular size"><span>A</span></button>
|
|
31
|
+
<button class="font-size-btn" data-size="large" title="Large size" aria-pressed="true"><span>A</span></button>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="header-divider"></div>
|
|
29
34
|
<button class="undo-btn" id="undoBtn" disabled title="Undo (Cmd+Z)">
|
|
30
35
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
|
|
31
36
|
</button>
|
|
@@ -49,6 +54,7 @@
|
|
|
49
54
|
<div id="fileContent"></div>
|
|
50
55
|
</div>
|
|
51
56
|
|
|
57
|
+
<div class="sidebar-drag-handle" id="sidebarDragHandle"></div>
|
|
52
58
|
<div class="sidebar" id="sidebar">
|
|
53
59
|
<div class="sidebar-header">
|
|
54
60
|
<h2>Comments <span class="comment-count" id="commentCount">0</span></h2>
|
|
@@ -84,13 +90,6 @@
|
|
|
84
90
|
</div>
|
|
85
91
|
</div>
|
|
86
92
|
|
|
87
|
-
<!-- Highlight popover (appears when clicking a comment highlight) -->
|
|
88
|
-
<div class="highlight-popover" id="highlightPopover" hidden>
|
|
89
|
-
<div class="highlight-popover-text" id="highlightPopoverText"></div>
|
|
90
|
-
<div class="highlight-popover-actions">
|
|
91
|
-
<button id="highlightEditBtn">Edit</button>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
93
|
|
|
95
94
|
<script type="module">
|
|
96
95
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
package/ui/styles.css
CHANGED
|
@@ -185,6 +185,53 @@ header h1 {
|
|
|
185
185
|
letter-spacing: 0.2px;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/* ---- Font size toggle ---- */
|
|
189
|
+
.font-size-toggle {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: 2px;
|
|
193
|
+
background: var(--bg);
|
|
194
|
+
border: 1px solid var(--border);
|
|
195
|
+
border-radius: var(--radius-sm);
|
|
196
|
+
padding: 2px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.font-size-btn {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
border: none;
|
|
204
|
+
background: transparent;
|
|
205
|
+
color: var(--text-faint);
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
border-radius: 6px;
|
|
208
|
+
padding: 3px 8px;
|
|
209
|
+
font-family: var(--font-sans);
|
|
210
|
+
font-weight: 600;
|
|
211
|
+
transition: all 0.15s ease;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.font-size-btn[data-size="regular"] span { font-size: 11px; }
|
|
215
|
+
.font-size-btn[data-size="large"] span { font-size: 15px; }
|
|
216
|
+
|
|
217
|
+
.font-size-btn:hover {
|
|
218
|
+
color: var(--text-dim);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.font-size-btn[aria-pressed="true"] {
|
|
222
|
+
background: var(--surface-hover);
|
|
223
|
+
color: var(--accent);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Large font size mode */
|
|
227
|
+
body.font-large #fileContent .markdown-body {
|
|
228
|
+
font-size: 19px !important;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
body.font-large #fileContent .plain-text {
|
|
232
|
+
font-size: 15px !important;
|
|
233
|
+
}
|
|
234
|
+
|
|
188
235
|
/* ---- Undo button ---- */
|
|
189
236
|
.undo-btn {
|
|
190
237
|
display: flex;
|
|
@@ -443,6 +490,7 @@ main {
|
|
|
443
490
|
.comment-highlight.resolved {
|
|
444
491
|
background: transparent;
|
|
445
492
|
border-bottom: none;
|
|
493
|
+
cursor: text;
|
|
446
494
|
}
|
|
447
495
|
|
|
448
496
|
[data-theme="light"] .comment-highlight:hover {
|
|
@@ -453,10 +501,31 @@ main {
|
|
|
453
501
|
background: rgba(58, 114, 160, 0.12);
|
|
454
502
|
}
|
|
455
503
|
|
|
504
|
+
/* ---- Sidebar drag handle ---- */
|
|
505
|
+
.sidebar-drag-handle {
|
|
506
|
+
width: 5px;
|
|
507
|
+
cursor: col-resize;
|
|
508
|
+
background: transparent;
|
|
509
|
+
flex-shrink: 0;
|
|
510
|
+
transition: background 0.15s ease;
|
|
511
|
+
z-index: 10;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.sidebar-drag-handle:hover,
|
|
515
|
+
body.sidebar-resizing .sidebar-drag-handle {
|
|
516
|
+
background: var(--accent);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
body.sidebar-resizing {
|
|
520
|
+
cursor: col-resize;
|
|
521
|
+
user-select: none;
|
|
522
|
+
}
|
|
523
|
+
|
|
456
524
|
/* ---- Sidebar ---- */
|
|
457
525
|
.sidebar {
|
|
458
|
-
width: 360px;
|
|
459
|
-
min-width:
|
|
526
|
+
width: var(--sidebar-width, 360px);
|
|
527
|
+
min-width: 300px;
|
|
528
|
+
max-width: 50vw;
|
|
460
529
|
background: var(--surface);
|
|
461
530
|
border-left: 1px solid var(--border);
|
|
462
531
|
overflow-y: auto;
|
|
@@ -596,12 +665,58 @@ main {
|
|
|
596
665
|
}
|
|
597
666
|
|
|
598
667
|
.comment-card.resolved {
|
|
599
|
-
opacity: 0.5;
|
|
600
668
|
border-left: 3px solid var(--green);
|
|
669
|
+
padding: 10px 14px;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.comment-card.resolved .comment-text,
|
|
673
|
+
.comment-card.resolved .comment-selected-text,
|
|
674
|
+
.comment-card.resolved .comment-meta {
|
|
675
|
+
color: var(--text-faint);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.comment-card.resolved .comment-selected-text {
|
|
679
|
+
border-left-color: var(--green);
|
|
680
|
+
background: var(--green-bg);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* Resolved collapsed summary */
|
|
684
|
+
.resolved-summary {
|
|
685
|
+
display: flex;
|
|
686
|
+
align-items: center;
|
|
687
|
+
gap: 8px;
|
|
688
|
+
cursor: pointer;
|
|
689
|
+
user-select: none;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.resolved-summary:hover .resolved-summary-text {
|
|
693
|
+
color: var(--text-dim);
|
|
601
694
|
}
|
|
602
695
|
|
|
603
|
-
.
|
|
604
|
-
|
|
696
|
+
.resolved-chevron {
|
|
697
|
+
flex-shrink: 0;
|
|
698
|
+
color: var(--text-faint);
|
|
699
|
+
transition: transform 0.15s ease;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.comment-card.resolved-expanded .resolved-chevron {
|
|
703
|
+
transform: rotate(90deg);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.resolved-summary-text {
|
|
707
|
+
font-size: 12px;
|
|
708
|
+
color: var(--text-faint);
|
|
709
|
+
white-space: nowrap;
|
|
710
|
+
overflow: hidden;
|
|
711
|
+
text-overflow: ellipsis;
|
|
712
|
+
min-width: 0;
|
|
713
|
+
transition: color 0.15s ease;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.resolved-details {
|
|
717
|
+
margin-top: 12px;
|
|
718
|
+
padding-top: 12px;
|
|
719
|
+
border-top: 1px solid var(--border);
|
|
605
720
|
}
|
|
606
721
|
|
|
607
722
|
@keyframes card-enter {
|
|
@@ -1269,6 +1384,130 @@ main {
|
|
|
1269
1384
|
padding-right: 2px;
|
|
1270
1385
|
}
|
|
1271
1386
|
|
|
1387
|
+
/* ---- Proposal diff ---- */
|
|
1388
|
+
.proposal-explanation {
|
|
1389
|
+
font-size: 12px;
|
|
1390
|
+
line-height: 1.5;
|
|
1391
|
+
margin-bottom: 8px;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
.proposal-diff {
|
|
1395
|
+
border: 1px solid var(--border);
|
|
1396
|
+
border-radius: var(--radius-sm);
|
|
1397
|
+
overflow: hidden;
|
|
1398
|
+
margin-bottom: 8px;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
.proposal-diff pre {
|
|
1402
|
+
margin: 0;
|
|
1403
|
+
padding: 8px 10px;
|
|
1404
|
+
font-family: var(--font-mono);
|
|
1405
|
+
font-size: 11px;
|
|
1406
|
+
line-height: 1.5;
|
|
1407
|
+
white-space: pre-wrap;
|
|
1408
|
+
word-wrap: break-word;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.proposal-label {
|
|
1412
|
+
display: block;
|
|
1413
|
+
font-family: var(--font-sans);
|
|
1414
|
+
font-size: 10px;
|
|
1415
|
+
font-weight: 600;
|
|
1416
|
+
text-transform: uppercase;
|
|
1417
|
+
letter-spacing: 0.5px;
|
|
1418
|
+
padding: 4px 10px 0;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
.proposal-old {
|
|
1422
|
+
background: rgba(212, 97, 110, 0.08);
|
|
1423
|
+
border-bottom: 1px solid var(--border);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.proposal-old .proposal-label { color: var(--red); }
|
|
1427
|
+
|
|
1428
|
+
.proposal-new {
|
|
1429
|
+
background: rgba(111, 191, 138, 0.08);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.proposal-new .proposal-label { color: var(--green); }
|
|
1433
|
+
|
|
1434
|
+
.proposal-actions {
|
|
1435
|
+
display: flex;
|
|
1436
|
+
gap: 6px;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
.proposal-apply-btn {
|
|
1440
|
+
font-family: var(--font-sans);
|
|
1441
|
+
font-size: 11px;
|
|
1442
|
+
font-weight: 600;
|
|
1443
|
+
padding: 5px 14px;
|
|
1444
|
+
border-radius: var(--radius-sm);
|
|
1445
|
+
border: none;
|
|
1446
|
+
background: var(--green);
|
|
1447
|
+
color: var(--bg);
|
|
1448
|
+
cursor: pointer;
|
|
1449
|
+
transition: opacity 0.15s ease;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
.proposal-apply-btn:hover { opacity: 0.85; }
|
|
1453
|
+
|
|
1454
|
+
.proposal-reject-btn {
|
|
1455
|
+
font-family: var(--font-sans);
|
|
1456
|
+
font-size: 11px;
|
|
1457
|
+
font-weight: 500;
|
|
1458
|
+
padding: 5px 14px;
|
|
1459
|
+
border-radius: var(--radius-sm);
|
|
1460
|
+
border: 1px solid var(--border);
|
|
1461
|
+
background: var(--surface);
|
|
1462
|
+
color: var(--text-dim);
|
|
1463
|
+
cursor: pointer;
|
|
1464
|
+
transition: all 0.15s ease;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
.proposal-reject-btn:hover {
|
|
1468
|
+
color: var(--red);
|
|
1469
|
+
border-color: var(--red);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
.proposal-status-badge {
|
|
1473
|
+
display: inline-block;
|
|
1474
|
+
font-size: 10px;
|
|
1475
|
+
font-weight: 600;
|
|
1476
|
+
padding: 3px 10px;
|
|
1477
|
+
border-radius: 100px;
|
|
1478
|
+
text-transform: uppercase;
|
|
1479
|
+
letter-spacing: 0.4px;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.proposal-status-badge.applied {
|
|
1483
|
+
color: var(--green);
|
|
1484
|
+
background: var(--green-bg);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
.proposal-status-badge.rejected {
|
|
1488
|
+
color: var(--red);
|
|
1489
|
+
background: rgba(212, 97, 110, 0.08);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
.proposal-reply.proposal-applied .proposal-diff {
|
|
1493
|
+
border-left: 3px solid var(--green);
|
|
1494
|
+
}
|
|
1272
1495
|
|
|
1496
|
+
.proposal-reply.proposal-rejected .proposal-diff {
|
|
1497
|
+
border-left: 3px solid var(--text-faint);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
.proposal-reply.proposal-rejected .proposal-diff pre {
|
|
1501
|
+
text-decoration: line-through;
|
|
1502
|
+
color: var(--text-faint);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
.proposal-reply.proposal-rejected .proposal-label {
|
|
1506
|
+
color: var(--text-faint);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
.proposal-reply.proposal-rejected .proposal-old {
|
|
1510
|
+
background: var(--surface-hover);
|
|
1511
|
+
}
|
|
1273
1512
|
|
|
1274
1513
|
|