@tenonhq/dovetail-dashboard 0.0.17 → 0.0.18

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.17",
3
+ "version": "0.0.18",
4
4
  "description": "Update Set Dashboard for Dovetail",
5
5
  "main": "server.js",
6
6
  "scripts": {
package/public/app.js CHANGED
@@ -1046,3 +1046,31 @@ document.getElementById("task-search").addEventListener("input", function (e) {
1046
1046
 
1047
1047
  // Render task toggles (My Tasks)
1048
1048
  renderTaskToggles();
1049
+
1050
+ // --- Theme toggle ---
1051
+
1052
+ function currentTheme() {
1053
+ return document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
1054
+ }
1055
+
1056
+ function applyTheme(theme) {
1057
+ document.documentElement.setAttribute("data-theme", theme);
1058
+ try {
1059
+ localStorage.setItem("cp-theme", theme);
1060
+ } catch (e) {}
1061
+ var btn = document.getElementById("cp-theme-toggle");
1062
+ if (btn) {
1063
+ btn.textContent = theme === "dark" ? "☀︎" : "☾";
1064
+ btn.title = theme === "dark" ? "Switch to light mode" : "Switch to dark mode";
1065
+ }
1066
+ }
1067
+
1068
+ (function setupTheme() {
1069
+ applyTheme(currentTheme());
1070
+ var btn = document.getElementById("cp-theme-toggle");
1071
+ if (btn) {
1072
+ btn.addEventListener("click", function () {
1073
+ applyTheme(currentTheme() === "dark" ? "light" : "dark");
1074
+ });
1075
+ }
1076
+ })();
@@ -961,3 +961,41 @@
961
961
  opacity: 1;
962
962
  transform: translateY(0);
963
963
  }
964
+
965
+ /* ════════════════════════════════════════════════════════════════════════════
966
+ Dark theme overrides.
967
+ The page already flows through the semantic tokens, so only the semantic
968
+ color chips that hardcode light pastels need explicit dark variants here.
969
+ The .cp-theme-toggle button itself is styled in styles.css (shared).
970
+ ════════════════════════════════════════════════════════════════════════════ */
971
+ [data-theme="dark"] .cp-status-DRAFT { background: #322d0c; color: #e8d860; border-color: #f4dc0033; }
972
+ [data-theme="dark"] .cp-status-APPROVED { background: #14301c; color: #8fe39d; border-color: #62dc4433; }
973
+
974
+ [data-theme="dark"] .cp-c-badge-success { background: #14301c; color: #8fe39d; border-color: #62dc4433; }
975
+ [data-theme="dark"] .cp-c-badge-warning { background: #322d0c; color: #e8d860; border-color: #f4dc0033; }
976
+ [data-theme="dark"] .cp-c-badge-danger { background: #371616; color: #f08f8f; border-color: #f4393933; }
977
+ [data-theme="dark"] .cp-c-badge-info { background: #0e2c3a; color: #8fd9f5; border-color: #76d6ff33; }
978
+
979
+ [data-theme="dark"] .cp-c-callout-info { background: #0e2c3a55; border-color: #76d6ff33; }
980
+ [data-theme="dark"] .cp-c-callout-success { background: #14301c55; border-color: #62dc4433; }
981
+ [data-theme="dark"] .cp-c-callout-warning { background: #322d0c55; border-color: #f4dc0033; }
982
+ [data-theme="dark"] .cp-c-callout-danger { background: #37161655; border-color: #f4393933; }
983
+ [data-theme="dark"] .cp-c-callout-info .cp-c-callout-icon { color: #8fd9f5; }
984
+ [data-theme="dark"] .cp-c-callout-success .cp-c-callout-icon { color: #8fe39d; }
985
+ [data-theme="dark"] .cp-c-callout-warning .cp-c-callout-icon { color: #e8d860; }
986
+ [data-theme="dark"] .cp-c-callout-danger .cp-c-callout-icon { color: #f08f8f; }
987
+
988
+ [data-theme="dark"] .cp-mermaid-error {
989
+ background: #37161655;
990
+ border-color: #f4393933;
991
+ }
992
+
993
+ [data-theme="dark"] .cp-pr-badge {
994
+ background: #0e2c3a;
995
+ color: #8fd9f5;
996
+ border-color: #76d6ff33;
997
+ }
998
+ [data-theme="dark"] .cp-pr-badge:hover {
999
+ background: #14384a;
1000
+ color: #b6e8ff;
1001
+ }
@@ -4,6 +4,17 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Dovetail — Claude Plans</title>
7
+ <script>
8
+ /* Apply the saved theme before first paint to avoid a flash of light. */
9
+ (function () {
10
+ try {
11
+ var t = localStorage.getItem("cp-theme");
12
+ if (t === "dark" || t === "light") {
13
+ document.documentElement.setAttribute("data-theme", t);
14
+ }
15
+ } catch (e) {}
16
+ })();
17
+ </script>
7
18
  <link rel="stylesheet" href="tokens.css" />
8
19
  <link rel="stylesheet" href="styles.css" />
9
20
  <link rel="stylesheet" href="claude-plans.css" />
@@ -19,6 +30,8 @@
19
30
  </div>
20
31
  <div class="header-right">
21
32
  <a class="cp-nav-link" href="/">&larr; Update Sets</a>
33
+ <button class="cp-theme-toggle" id="cp-theme-toggle" type="button"
34
+ aria-label="Toggle dark mode" title="Toggle dark mode"></button>
22
35
  <span class="cp-storage" id="cp-storage" title="storage root"></span>
23
36
  </div>
24
37
  </header>
@@ -7,9 +7,21 @@
7
7
  (function () {
8
8
  "use strict";
9
9
 
10
- if (window.mermaid && typeof window.mermaid.initialize === "function") {
11
- window.mermaid.initialize({ startOnLoad: false, theme: "default", securityLevel: "strict" });
10
+ function currentTheme() {
11
+ return document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
12
12
  }
13
+
14
+ function initMermaid() {
15
+ if (window.mermaid && typeof window.mermaid.initialize === "function") {
16
+ window.mermaid.initialize({
17
+ startOnLoad: false,
18
+ theme: currentTheme() === "dark" ? "dark" : "default",
19
+ securityLevel: "strict"
20
+ });
21
+ }
22
+ }
23
+ initMermaid();
24
+
13
25
  if (window.marked && typeof window.marked.setOptions === "function") {
14
26
  window.marked.setOptions({ breaks: true, gfm: true });
15
27
  }
@@ -472,7 +484,32 @@
472
484
  es.onerror = function () { /* EventSource auto-reconnects */ };
473
485
  }
474
486
 
487
+ /* ─── Theme toggle ─────────────────────────────────────────────────────────── */
488
+
489
+ function applyTheme(theme) {
490
+ document.documentElement.setAttribute("data-theme", theme);
491
+ try { localStorage.setItem("cp-theme", theme); } catch (_) {}
492
+ var btn = document.getElementById("cp-theme-toggle");
493
+ if (btn) {
494
+ btn.textContent = theme === "dark" ? "☀︎" : "☾";
495
+ btn.title = theme === "dark" ? "Switch to light mode" : "Switch to dark mode";
496
+ }
497
+ initMermaid();
498
+ }
499
+
500
+ function setupTheme() {
501
+ applyTheme(currentTheme());
502
+ var btn = document.getElementById("cp-theme-toggle");
503
+ if (btn) {
504
+ btn.addEventListener("click", function () {
505
+ applyTheme(currentTheme() === "dark" ? "light" : "dark");
506
+ if (state.selectedSlug) renderDetail();
507
+ });
508
+ }
509
+ }
510
+
475
511
  document.addEventListener("DOMContentLoaded", function () {
512
+ setupTheme();
476
513
  setActiveTab("plan");
477
514
  loadInitial().then(startStream);
478
515
  });
package/public/index.html CHANGED
@@ -4,6 +4,17 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Dovetail — Update Sets</title>
7
+ <script>
8
+ /* Apply the saved theme before first paint to avoid a flash of light. */
9
+ (function () {
10
+ try {
11
+ var t = localStorage.getItem("cp-theme");
12
+ if (t === "dark" || t === "light") {
13
+ document.documentElement.setAttribute("data-theme", t);
14
+ }
15
+ } catch (e) {}
16
+ })();
17
+ </script>
7
18
  <link rel="stylesheet" href="tokens.css">
8
19
  <link rel="stylesheet" href="styles.css">
9
20
  </head>
@@ -20,6 +31,8 @@
20
31
  <div class="header-right">
21
32
  <button class="btn-refresh" id="refresh-btn" title="Refresh scopes and update sets">Refresh</button>
22
33
  <a class="btn-refresh" href="/claude-plans" title="Claude plans and diagrams pushed via MCP">Claude Plans</a>
34
+ <button class="cp-theme-toggle" id="cp-theme-toggle" type="button"
35
+ aria-label="Toggle dark mode" title="Toggle dark mode"></button>
23
36
  <div class="active-task-chip" id="active-task-chip" style="display:none;"></div>
24
37
  <div class="instance-badge" id="instance-badge">Loading...</div>
25
38
  </div>
package/public/styles.css CHANGED
@@ -887,3 +887,31 @@ header h1 span {
887
887
  .recent-edit-dismiss:hover {
888
888
  color: var(--danger);
889
889
  }
890
+
891
+ /* ── Theme toggle button (header) — shared by dashboard + Claude Plans ──── */
892
+ .cp-theme-toggle {
893
+ display: inline-flex;
894
+ align-items: center;
895
+ justify-content: center;
896
+ width: 32px;
897
+ height: 32px;
898
+ padding: 0;
899
+ font-size: 14px;
900
+ line-height: 1;
901
+ background: var(--surface-raised);
902
+ color: var(--fg-muted);
903
+ border: 1px solid var(--border-strong);
904
+ border-radius: var(--radius-md);
905
+ cursor: pointer;
906
+ transition: background var(--dur-1) var(--ease-out),
907
+ color var(--dur-1) var(--ease-out);
908
+ }
909
+ .cp-theme-toggle:hover {
910
+ background: var(--bg-subtle);
911
+ color: var(--fg);
912
+ }
913
+ .cp-theme-toggle:focus-visible {
914
+ outline: none;
915
+ border-color: var(--border-focus);
916
+ box-shadow: 0 0 0 3px rgba(50, 181, 127, 0.18);
917
+ }
package/public/tokens.css CHANGED
@@ -160,3 +160,64 @@
160
160
  --av-pink-bg: #fb61fe; --av-pink-fg: #fbf8f3;
161
161
  --av-earthy-bg: #dcd4c0; --av-earthy-fg: #987e59;
162
162
  }
163
+
164
+ /* ============================================================================
165
+ Dark theme — activated by <html data-theme="dark">.
166
+ Used by the Claude Plans page; safe to adopt dashboard-wide later.
167
+ Overrides the semantic aliases only — the raw color scales above are shared.
168
+ Brand emerald and neon accent are preserved; grounds invert to dark woodgrain.
169
+ ============================================================================ */
170
+ [data-theme="dark"] {
171
+ --bg: #0d1410;
172
+ --bg-subtle: #121c16;
173
+ --bg-muted: #18241c;
174
+ --bg-paper: #121c16;
175
+ --bg-oak: #20302554;
176
+ --bg-deep: #060b08;
177
+ --bg-overlay: rgba(0, 0, 0, 0.66);
178
+
179
+ --surface: #121c16;
180
+ --surface-raised: #18241c;
181
+
182
+ --fg: #e9efe9;
183
+ --fg-muted: #8da095;
184
+ --fg-subtle: #5f7167;
185
+ --fg-on-dark: #e9efe9;
186
+ --fg-on-neon: var(--emerald-700);
187
+
188
+ --accent: var(--neon-300);
189
+ --accent-hover: var(--neon-500);
190
+ --accent-press: var(--neon-700);
191
+ --accent-fg: #0d1410;
192
+
193
+ --brand: var(--emerald-50);
194
+ --brand-deep: #e9efe9;
195
+
196
+ --success: var(--success-300);
197
+ --warning: var(--warning-300);
198
+ --danger: var(--error-300);
199
+ --danger-hover: var(--error-500);
200
+ --info: var(--blue-300);
201
+
202
+ --border: #26342b;
203
+ --border-strong: #344538;
204
+ --border-subtle: #1d2922;
205
+ --border-focus: var(--neon-500);
206
+
207
+ --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
208
+ --shadow-2: 0 4px 12px rgba(0, 0, 0, 0.5);
209
+ --shadow-3: 0 10px 34px rgba(0, 0, 0, 0.6);
210
+ --shadow-pop: 0 12px 40px rgba(0, 0, 0, 0.7);
211
+
212
+ /* ===== Tag color pairs — dark grounds, light ink ===================== */
213
+ --tag-green-bg: #14301c; --tag-green-fg: #8fe39d;
214
+ --tag-blue-bg: #182040; --tag-blue-fg: #aab4ff;
215
+ --tag-cyan-bg: #0e2c3a; --tag-cyan-fg: #8fd9f5;
216
+ --tag-sage-bg: #1d2922; --tag-sage-fg: #b6c6bc;
217
+ --tag-warm-bg: #2c2417; --tag-warm-fg: #d8c4a2;
218
+ --tag-yellow-bg: #322d0c; --tag-yellow-fg: #e8d860;
219
+ --tag-purple-bg: #2a1633; --tag-purple-fg: #e29bf0;
220
+ --tag-orange-bg: #331c0c; --tag-orange-fg: #f0a878;
221
+ --tag-red-bg: #371616; --tag-red-fg: #f08f8f;
222
+ --tag-teal-bg: #103027; --tag-teal-fg: #6fd1ad;
223
+ }