design-share 0.2.0 → 0.2.2

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": "design-share",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Live branch previews and pinned comments for design teams. Zero config, no server, git is the backend.",
5
5
  "keywords": ["design", "preview", "branches", "collaboration", "cli"],
6
6
  "license": "MIT",
package/public/app.js CHANGED
@@ -188,12 +188,20 @@ function branchRow({ user, branch, time, dim, own }) {
188
188
  else if (isOwnServing && prev && prev.status === 'ready') dotCls = 'live';
189
189
 
190
190
  const unresolved = openCount(branch);
191
+ const pr = b.prs && b.prs[branch];
191
192
  btn.innerHTML = `
192
193
  <span class="dot ${dotCls}"></span>
193
194
  <span class="branch-label">${escapeHtml(branch)}</span>
194
195
  ${unresolved ? `<span class="badge">${unresolved}</span>` : ''}
196
+ ${pr ? `<span class="pr-link" title="${escapeHtml(`PR #${pr.number}: ${pr.title || ''}`)}">#${pr.number}</span>` : ''}
195
197
  <span class="meta">${timeAgo(time)}</span>`;
196
198
  btn.onclick = () => select(user, branch);
199
+ if (pr) {
200
+ btn.querySelector('.pr-link').onclick = (e) => {
201
+ e.stopPropagation();
202
+ window.open(pr.url, '_blank');
203
+ };
204
+ }
197
205
  return btn;
198
206
  }
199
207
 
package/public/style.css CHANGED
@@ -123,6 +123,18 @@ button { font: inherit; cursor: pointer; }
123
123
  .branch-row .meta { font-size: 11px; color: var(--text-3); flex: none; }
124
124
  .branch-row.dim { color: var(--text-2); }
125
125
 
126
+ .pr-link {
127
+ font-size: 10.5px;
128
+ color: var(--text-3);
129
+ border: 1px solid var(--border);
130
+ border-radius: 99px;
131
+ padding: 0 6px;
132
+ line-height: 16px;
133
+ flex: none;
134
+ background: #fff;
135
+ }
136
+ .pr-link:hover { color: var(--accent); border-color: var(--accent); }
137
+
126
138
  .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
127
139
  .dot.live { background: var(--green); }
128
140
  .dot.idle { background: #d4d4d8; }
package/src/github.js ADDED
@@ -0,0 +1,24 @@
1
+ import { execFile } from 'node:child_process';
2
+
3
+ // PR links come from the gh CLI so no tokens are stored or handled: gh uses
4
+ // whoever is already logged in. Machines without gh (or without auth) simply
5
+ // return an empty map and the dashboard shows no PR links.
6
+ export function listOpenPRs(repoRoot) {
7
+ return new Promise((resolve) => {
8
+ execFile('gh', [
9
+ 'pr', 'list', '--state', 'open', '--limit', '200',
10
+ '--json', 'number,url,headRefName,title',
11
+ ], { cwd: repoRoot, timeout: 15_000 }, (err, stdout) => {
12
+ if (err) return resolve({});
13
+ try {
14
+ const byBranch = {};
15
+ for (const pr of JSON.parse(stdout)) {
16
+ byBranch[pr.headRefName] = { number: pr.number, url: pr.url, title: pr.title };
17
+ }
18
+ resolve(byBranch);
19
+ } catch {
20
+ resolve({});
21
+ }
22
+ });
23
+ });
24
+ }
package/src/server.js CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import { tryGit } from './git.js';
6
6
  import { currentBranch } from './detect.js';
7
7
  import { serveStatic } from './previews.js';
8
+ import { listOpenPRs } from './github.js';
8
9
 
9
10
  const PUBLIC_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'public');
10
11
  const SYNC_INTERVAL_MS = 15_000;
@@ -36,6 +37,11 @@ export class DesignShareServer {
36
37
  this.port = port;
37
38
  this.server = null;
38
39
  this.timers = [];
40
+ this.prs = {};
41
+ }
42
+
43
+ async refreshPRs() {
44
+ this.prs = await listOpenPRs(this.repo.root);
39
45
  }
40
46
 
41
47
  async branchHeads() {
@@ -84,6 +90,7 @@ export class DesignShareServer {
84
90
  otherBranches: others,
85
91
  comments: Object.values(state.comments || {}),
86
92
  previews: this.previews.statuses(),
93
+ prs: this.prs,
87
94
  sync: {
88
95
  hasRemote: this.store.hasRemote,
89
96
  lastSync: this.store.lastSync,
@@ -202,6 +209,8 @@ export class DesignShareServer {
202
209
  () => tryGit(this.repo.root, ['fetch', '--quiet', 'origin']),
203
210
  FETCH_INTERVAL_MS,
204
211
  ));
212
+ this.refreshPRs();
213
+ this.timers.push(setInterval(() => this.refreshPRs(), 180_000));
205
214
  resolve(port);
206
215
  });
207
216
  };