@tenonhq/dovetail-dashboard 0.0.14 → 0.0.16

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": "@tenonhq/dovetail-dashboard",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Update Set Dashboard for Dovetail",
5
5
  "main": "server.js",
6
6
  "scripts": {
@@ -105,7 +105,7 @@
105
105
  .cp-detail-header { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
106
106
  .cp-detail-header h2 { font-size: 20px; margin: 0; }
107
107
  .cp-stamp { font-size: 11px; color: var(--text-muted); margin-left: auto; }
108
- .cp-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
108
+ .cp-tabs { display: flex; gap: 0; align-items: center; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
109
109
  .cp-tab {
110
110
  background: transparent;
111
111
  border: none;
@@ -148,8 +148,8 @@
148
148
  }
149
149
  .cp-artifact-head {
150
150
  display: flex;
151
- justify-content: space-between;
152
151
  align-items: center;
152
+ gap: 8px;
153
153
  margin-bottom: 12px;
154
154
  }
155
155
  .cp-artifact-title { font-weight: 600; font-size: 14px; }
@@ -641,3 +641,49 @@
641
641
  white-space: nowrap;
642
642
  }
643
643
  .cp-pr-badge:hover { background: rgba(100,140,190,0.25); color: var(--text); }
644
+
645
+ /* ─── Resume button in plan detail header ──────────────────────────────────── */
646
+ .cp-resume-btn {
647
+ display: inline-flex;
648
+ align-items: center;
649
+ padding: 3px 10px;
650
+ border-radius: 4px;
651
+ font-size: 12px;
652
+ font-family: inherit;
653
+ font-weight: 600;
654
+ letter-spacing: 0.02em;
655
+ background: rgba(74, 140, 92, 0.15);
656
+ color: #92d3a4;
657
+ border: 1px solid rgba(74, 140, 92, 0.4);
658
+ cursor: pointer;
659
+ white-space: nowrap;
660
+ transition: background 0.15s, color 0.15s;
661
+ }
662
+ .cp-resume-btn:hover {
663
+ background: rgba(74, 140, 92, 0.28);
664
+ color: var(--text);
665
+ }
666
+
667
+ /* ─── Toast notification ────────────────────────────────────────────────────── */
668
+ .cp-toast {
669
+ position: fixed;
670
+ bottom: 20px;
671
+ right: 20px;
672
+ background: var(--card-bg);
673
+ border: 1px solid rgba(74, 140, 92, 0.5);
674
+ border-left: 3px solid #4a8c5c;
675
+ color: #92d3a4;
676
+ padding: 9px 16px;
677
+ border-radius: 4px;
678
+ font-size: 13px;
679
+ font-family: inherit;
680
+ z-index: 9999;
681
+ opacity: 0;
682
+ transform: translateY(6px);
683
+ transition: opacity 0.2s, transform 0.2s;
684
+ pointer-events: none;
685
+ }
686
+ .cp-toast--visible {
687
+ opacity: 1;
688
+ transform: translateY(0);
689
+ }
@@ -66,6 +66,18 @@
66
66
  setTimeout(function () { el.remove(); }, 4000);
67
67
  }
68
68
 
69
+ function showToast(msg) {
70
+ var el = document.createElement("div");
71
+ el.className = "cp-toast";
72
+ el.textContent = msg;
73
+ document.body.appendChild(el);
74
+ setTimeout(function () { el.classList.add("cp-toast--visible"); }, 10);
75
+ setTimeout(function () {
76
+ el.classList.remove("cp-toast--visible");
77
+ setTimeout(function () { el.remove(); }, 300);
78
+ }, 2200);
79
+ }
80
+
69
81
  function renderMarkdown(md, target) {
70
82
  if (!window.marked || !window.DOMPurify) {
71
83
  target.textContent = md;
@@ -99,6 +111,84 @@
99
111
  }
100
112
  }
101
113
 
114
+ /* ─── Copy helpers ─────────────────────────────────────────────────────────── */
115
+
116
+ function fallbackCopy(text) {
117
+ var ta = document.createElement("textarea");
118
+ ta.value = text;
119
+ ta.style.cssText = "position:fixed;left:-9999px;top:-9999px;opacity:0";
120
+ document.body.appendChild(ta);
121
+ ta.select();
122
+ try { document.execCommand("copy"); } catch (_) {}
123
+ ta.remove();
124
+ }
125
+
126
+ function makeCopyBtn(label, getText) {
127
+ var btn = document.createElement("button");
128
+ btn.className = "cp-copy-btn";
129
+ btn.textContent = label;
130
+ btn.addEventListener("click", function (e) {
131
+ e.stopPropagation();
132
+ var text = getText();
133
+ var flash = function () {
134
+ btn.textContent = "Copied!";
135
+ btn.classList.add("cp-copy-btn--copied");
136
+ setTimeout(function () {
137
+ btn.textContent = label;
138
+ btn.classList.remove("cp-copy-btn--copied");
139
+ }, 1500);
140
+ };
141
+ if (navigator.clipboard && navigator.clipboard.writeText) {
142
+ navigator.clipboard.writeText(text).then(flash).catch(function () {
143
+ fallbackCopy(text);
144
+ flash();
145
+ });
146
+ } else {
147
+ fallbackCopy(text);
148
+ flash();
149
+ }
150
+ });
151
+ return btn;
152
+ }
153
+
154
+ function addTabsCopyAll(plan, artifacts) {
155
+ var existing = document.getElementById("cp-tabs-copy-all");
156
+ if (existing) existing.remove();
157
+
158
+ var btn = makeCopyBtn("Copy All", function () {
159
+ if (state.activeTab === "artifacts") {
160
+ return artifacts.map(function (a) {
161
+ return "# " + a.title + "\n\n" + a.content;
162
+ }).join("\n\n---\n\n");
163
+ }
164
+ return plan.content_md && plan.content_md.trim()
165
+ ? plan.content_md
166
+ : els.planPanel.innerText.trim();
167
+ });
168
+ btn.id = "cp-tabs-copy-all";
169
+ btn.style.marginLeft = "auto";
170
+ var tabsEl = document.querySelector(".cp-tabs");
171
+ if (tabsEl) tabsEl.appendChild(btn);
172
+ }
173
+
174
+ function addPlanSectionCopyBtns() {
175
+ var structured = els.planPanel.querySelector(".cp-structured");
176
+ if (!structured) return;
177
+ var children = Array.from(structured.children);
178
+ children.forEach(function (child) {
179
+ child.classList.add("cp-c-copy-wrap");
180
+ var group = document.createElement("div");
181
+ group.className = "cp-copy-btn-group";
182
+ var btn = makeCopyBtn("Copy", (function (el) {
183
+ return function () { return el.innerText.trim(); };
184
+ })(child));
185
+ group.appendChild(btn);
186
+ child.appendChild(group);
187
+ });
188
+ }
189
+
190
+ /* ─────────────────────────────────────────────────────────────────────────── */
191
+
102
192
  function renderRail() {
103
193
  var plans = sortedPlans();
104
194
  els.count.textContent = String(plans.length);
@@ -165,6 +255,8 @@
165
255
  if (!state.selectedSlug || !state.plans.has(state.selectedSlug)) {
166
256
  els.detailEmpty.style.display = "block";
167
257
  els.detailBody.hidden = true;
258
+ var orphan = document.getElementById("cp-tabs-copy-all");
259
+ if (orphan) orphan.remove();
168
260
  return;
169
261
  }
170
262
  var plan = state.plans.get(state.selectedSlug);
@@ -193,6 +285,28 @@
193
285
  els.detailStamp.insertAdjacentElement("afterend", prBadge);
194
286
  }
195
287
 
288
+ var existingResumeBtn = document.getElementById("cp-resume-btn");
289
+ if (existingResumeBtn) existingResumeBtn.remove();
290
+ var resumeBtn = document.createElement("button");
291
+ resumeBtn.id = "cp-resume-btn";
292
+ resumeBtn.className = "cp-resume-btn";
293
+ resumeBtn.textContent = "Resume";
294
+ resumeBtn.title = "Copy /resume command to clipboard";
295
+ resumeBtn.addEventListener("click", function () {
296
+ var cmd = "/resume " + plan.slug;
297
+ var finish = function () { showToast("Copied! Paste into Claude."); };
298
+ if (navigator.clipboard && navigator.clipboard.writeText) {
299
+ navigator.clipboard.writeText(cmd).then(finish).catch(function () {
300
+ fallbackCopy(cmd);
301
+ finish();
302
+ });
303
+ } else {
304
+ fallbackCopy(cmd);
305
+ finish();
306
+ }
307
+ });
308
+ els.detailStatus.insertAdjacentElement("afterend", resumeBtn);
309
+
196
310
  if (plan.content_html) {
197
311
  els.planPanel.innerHTML = window.DOMPurify
198
312
  ? window.DOMPurify.sanitize(plan.content_html)
@@ -201,6 +315,9 @@
201
315
  renderMarkdown(plan.content_md, els.planPanel);
202
316
  }
203
317
 
318
+ addPlanSectionCopyBtns();
319
+ addTabsCopyAll(plan, artifacts);
320
+
204
321
  els.artifactsPanel.innerHTML = "";
205
322
  if (artifacts.length === 0) {
206
323
  var empty = document.createElement("div");
@@ -222,6 +339,14 @@
222
339
  kind.textContent = artifact.kind;
223
340
  head.appendChild(title);
224
341
  head.appendChild(kind);
342
+
343
+ var copyGroup = document.createElement("div");
344
+ copyGroup.className = "cp-copy-btn-group cp-copy-btn-group--artifact";
345
+ copyGroup.appendChild(makeCopyBtn("Copy", (function (content) {
346
+ return function () { return content; };
347
+ })(artifact.content)));
348
+ head.appendChild(copyGroup);
349
+
225
350
  card.appendChild(head);
226
351
 
227
352
  var body = document.createElement("div");