Files
orca/docs/design/mockups/github-project-view.html
T

612 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mockup — GitHub Project view (Tasks)</title>
<style>
:root {
--bg: #0d1117;
--panel: #0d1117;
--panel-2: #161b22;
--border: #30363d;
--border-muted: #21262d;
--fg: #e6edf3;
--fg-muted: #8d96a0;
--fg-dim: #6e7681;
--accent: #2f81f7;
--green: #3fb950;
--red: #f85149;
--purple: #a371f7;
--yellow: #d29922;
--open: #3fb950;
--closed: #a371f7;
--draft: #6e7681;
--row-hover: #161b22;
--pill: #21262d;
}
* { box-sizing: border-box; }
html, body {
margin: 0; padding: 0; background: var(--bg); color: var(--fg);
font: 13px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
.app { max-width: 1360px; margin: 0 auto; padding: 16px 20px 48px; }
/* Source + mode toggle bar */
.bar {
display: flex; align-items: center; gap: 10px;
padding: 8px 0 12px; border-bottom: 1px solid var(--border-muted);
}
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.seg button {
background: transparent; color: var(--fg-muted); border: 0; padding: 5px 10px;
font-size: 12px; cursor: pointer; display: inline-flex; gap: 6px; align-items: center;
}
.seg button.active { background: var(--panel-2); color: var(--fg); }
.seg button + button { border-left: 1px solid var(--border); }
/* GitHub tab panel: holds the mode toggle plus all GitHub-scoped content.
The mode toggle lives inside this panel (not top-level) so it reads as
"sub-nav within GitHub" rather than a peer of the source tabs. */
.gh-panel {
margin-top: 10px;
border: 1px solid var(--border-muted);
border-radius: 8px;
background: var(--panel);
}
.gh-panel .mode-bar {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px; border-bottom: 1px solid var(--border-muted);
background: #0b1017;
border-top-left-radius: 8px; border-top-right-radius: 8px;
}
.gh-panel .mode-bar .seg button { padding: 4px 10px; }
.gh-panel-body { padding: 0 12px 12px; }
.spacer { flex: 1; }
/* Project header */
.proj-header {
display: flex; align-items: center; gap: 10px;
padding: 14px 0 10px;
}
.picker {
display: inline-flex; align-items: center; gap: 8px;
background: var(--panel-2); border: 1px solid var(--border); color: var(--fg);
border-radius: 6px; padding: 5px 9px; cursor: pointer; font-weight: 600;
}
.picker .owner { color: var(--fg-muted); font-weight: 500; }
.picker .caret { color: var(--fg-muted); }
.chip {
display: inline-flex; align-items: center; gap: 6px;
background: var(--pill); border: 1px solid var(--border-muted);
color: var(--fg); border-radius: 999px; padding: 3px 9px; font-size: 12px;
}
.chip .lbl { color: var(--fg-muted); }
.filter {
color: var(--fg-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px; padding: 3px 8px; border-radius: 5px; background: #0b1220;
border: 1px solid var(--border-muted); max-width: 360px; overflow: hidden;
text-overflow: ellipsis; white-space: nowrap;
}
.count {
display: inline-flex; align-items: center; gap: 6px;
background: var(--pill); color: var(--fg-muted); border-radius: 999px;
padding: 3px 9px; font-size: 12px;
}
.count b { color: var(--fg); font-weight: 600; }
.iconbtn {
background: transparent; border: 1px solid var(--border); color: var(--fg-muted);
width: 28px; height: 28px; border-radius: 6px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
}
.iconbtn:hover { color: var(--fg); background: var(--panel-2); }
/* List */
.list { margin-top: 8px; border: 1px solid var(--border-muted); border-radius: 8px; overflow: hidden; }
.group + .group { border-top: 1px solid var(--border-muted); }
.group-header {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px; background: #0f141b;
border-bottom: 1px solid var(--border-muted);
user-select: none; cursor: pointer;
}
.group-header .chev { color: var(--fg-muted); transition: transform .12s; display: inline-block; }
.group-header.collapsed .chev { transform: rotate(-90deg); }
.group-header .name { font-weight: 600; }
.group-header .range { color: var(--fg-dim); font-size: 12px; }
.group-header .cnt {
margin-left: auto;
background: var(--pill); color: var(--fg-muted);
border-radius: 999px; padding: 1px 8px; font-size: 11px;
}
.pill-current {
background: rgba(63,185,80,.15); color: var(--green);
border: 1px solid rgba(63,185,80,.35);
border-radius: 999px; padding: 1px 7px; font-size: 11px;
}
/* Column header */
.colhead, .row {
display: grid;
grid-template-columns:
minmax(320px, 3fr) /* Title */
120px /* Status */
96px /* Priority */
72px /* Size */
minmax(160px, 1.2fr) /* Sprint */
minmax(140px, 1fr) /* Repository */
140px; /* Assignees */
align-items: center;
gap: 0;
padding: 6px 12px;
}
.colhead {
color: var(--fg-muted); font-size: 11px; text-transform: uppercase;
letter-spacing: .04em; background: #0b1017; border-bottom: 1px solid var(--border-muted);
}
.row {
border-bottom: 1px solid var(--border-muted);
position: relative; cursor: pointer;
}
.row:hover { background: var(--row-hover); }
.row:last-child { border-bottom: 0; }
/* Title cell */
.title-cell { display: flex; align-items: center; gap: 8px; min-width: 0; }
.state {
width: 14px; height: 14px; border-radius: 50%; flex: 0 0 14px;
border: 2px solid var(--open); background: transparent;
}
.state.closed { border-color: var(--closed); background: var(--closed); }
.state.merged { border-color: #a371f7; background: #a371f7; }
.state.draft { border-color: var(--draft); background: transparent; }
.state.pr { border-radius: 3px; }
.title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.title-text a { color: var(--fg); text-decoration: none; }
.title-text a:hover { text-decoration: underline; }
.num { color: var(--fg-dim); margin-left: 6px; }
/* Pills */
.sel {
display: inline-flex; align-items: center; gap: 6px;
padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 500;
border: 1px solid transparent;
}
.sel::before {
content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor;
opacity: .9;
}
.sel.p0 { color: #f85149; background: rgba(248,81,73,.1); border-color: rgba(248,81,73,.25); }
.sel.p1 { color: #d29922; background: rgba(210,153,34,.12); border-color: rgba(210,153,34,.25); }
.sel.p2 { color: #3fb950; background: rgba(63,185,80,.1); border-color: rgba(63,185,80,.25); }
.sel.xs { color: #58a6ff; background: rgba(88,166,255,.1); border-color: rgba(88,166,255,.25); }
.sel.s { color: #a371f7; background: rgba(163,113,247,.1); border-color: rgba(163,113,247,.25); }
.sel.m { color: #d29922; background: rgba(210,153,34,.12); border-color: rgba(210,153,34,.25); }
.sel.l { color: #f85149; background: rgba(248,81,73,.1); border-color: rgba(248,81,73,.25); }
.status-inprog { color: #58a6ff; background: rgba(88,166,255,.1); border-color: rgba(88,166,255,.25); }
.status-todo { color: var(--fg-muted); background: var(--pill); border-color: var(--border-muted); }
.status-rev { color: #a371f7; background: rgba(163,113,247,.1); border-color: rgba(163,113,247,.25); }
.iter {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; color: var(--fg);
}
.iter .dates { color: var(--fg-dim); font-size: 11px; }
.repo { color: var(--fg-muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avatars { display: inline-flex; }
.avatar {
width: 22px; height: 22px; border-radius: 50%;
background: linear-gradient(135deg, #2f81f7, #a371f7);
border: 2px solid var(--bg); display: inline-flex; align-items: center;
justify-content: center; font-size: 10px; font-weight: 600; color: #fff;
margin-left: -6px;
}
.avatar:first-child { margin-left: 0; }
.avatar.gray { background: linear-gradient(135deg, #6e7681, #484f58); }
.avatar.green { background: linear-gradient(135deg, #3fb950, #238636); }
.avatar.red { background: linear-gradient(135deg, #f85149, #da3633); }
/* Hover row actions */
.row-actions {
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
display: none; gap: 4px;
background: linear-gradient(90deg, transparent 0%, var(--row-hover) 25%);
padding-left: 24px;
}
.row:hover .row-actions { display: inline-flex; }
.row-actions button {
background: var(--panel-2); border: 1px solid var(--border); color: var(--fg-muted);
width: 26px; height: 26px; border-radius: 6px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
}
.row-actions button:hover { color: var(--fg); background: #1f2630; }
/* Annotations */
.note {
color: var(--fg-dim); font-size: 11px; padding: 10px 14px;
border-top: 1px dashed var(--border-muted); background: #0b1017;
}
.note b { color: var(--fg-muted); font-weight: 600; }
/* Picker popover */
.overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.4);
display: none; align-items: flex-start; justify-content: center; padding-top: 80px;
z-index: 10;
}
.overlay.open { display: flex; }
.popover {
width: 480px; max-height: 70vh; overflow: auto;
background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px;
box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
.popover .search {
padding: 10px; border-bottom: 1px solid var(--border-muted);
}
.popover input {
width: 100%; background: #0b1017; border: 1px solid var(--border);
color: var(--fg); border-radius: 6px; padding: 6px 10px; font-size: 13px;
}
.sec-label {
padding: 8px 12px 4px; font-size: 11px; color: var(--fg-dim);
text-transform: uppercase; letter-spacing: .05em;
}
.pitem {
display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer;
}
.pitem:hover { background: #1f2630; }
.pitem .title { font-weight: 500; }
.pitem .meta { color: var(--fg-dim); font-size: 12px; margin-left: auto; }
.pitem .warn { color: var(--yellow); font-size: 11px; }
.pitem.current { background: rgba(47,129,247,.12); }
.paste {
border-top: 1px solid var(--border-muted); padding: 10px 12px;
}
/* Non-table layout states */
.state-panel {
border: 1px solid var(--border-muted); border-radius: 8px;
padding: 28px; text-align: center; color: var(--fg-muted);
margin-top: 16px;
}
.state-panel .head { color: var(--fg); font-weight: 600; margin-bottom: 6px; }
.state-panel .actions { margin-top: 12px; display: inline-flex; gap: 8px; }
.btn {
background: var(--panel-2); border: 1px solid var(--border); color: var(--fg);
padding: 5px 12px; border-radius: 6px; cursor: pointer; font-size: 12px;
}
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.section-title {
margin: 28px 0 8px; color: var(--fg-muted);
font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
}
/* Banner */
.banner {
margin-top: 8px; padding: 10px 12px;
border: 1px solid rgba(248,81,73,.35); background: rgba(248,81,73,.08);
color: #ffb4ad; border-radius: 8px; font-size: 12px;
display: flex; align-items: center; gap: 10px;
}
.banner .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #fff; }
.banner .grow { flex: 1; }
code.kbd {
background: #0b1017; border: 1px solid var(--border-muted);
padding: 1px 5px; border-radius: 4px; font-size: 11px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
</style>
</head>
<body>
<div class="app">
<!-- Top-level source tabs only. The mode toggle is NOT a peer here. -->
<div class="bar">
<div class="seg" role="tablist" aria-label="Source">
<button class="active" id="srcGh">GitHub</button>
<button id="srcLinear">Linear</button>
</div>
<div class="spacer"></div>
<button class="iconbtn" title="Refresh" aria-label="Refresh">&#x21bb;</button>
</div>
<!-- GitHub tab panel. Mode toggle lives inside the panel. -->
<div class="gh-panel">
<div class="mode-bar" role="tablist" aria-label="GitHub mode">
<div class="seg">
<button>Issues/PRs</button>
<button class="active">Project</button>
</div>
</div>
<div class="gh-panel-body">
<!-- Project header -->
<div class="proj-header">
<button class="picker" id="pickerBtn" aria-haspopup="dialog">
<span class="owner">stablyai /</span>
<span>Stably Product Board</span>
<span class="caret">▾</span>
</button>
<span class="chip"><span class="lbl">View:</span><b>Me</b></span>
<span class="filter" title="assignee:@me is:open no:triage-group">assignee:@me is:open no:triage-group</span>
<span class="count"><b>127</b>&nbsp;items</span>
<div class="spacer"></div>
<button class="iconbtn" title="Open in GitHub" aria-label="Open in GitHub">↗</button>
</div>
<!-- Row list -->
<div class="list" role="grid" aria-label="Project rows">
<div class="colhead" role="row">
<div>Title</div>
<div>Status</div>
<div>Priority</div>
<div>Size</div>
<div>Sprint</div>
<div>Repository</div>
<div style="justify-self:end">Assignees</div>
</div>
<!-- Group: Sprint 42 (current) -->
<div class="group">
<div class="group-header" role="row" tabindex="0">
<span class="chev">▾</span>
<span class="name">Sprint 42</span>
<span class="range">Apr 28 – May 12</span>
<span class="pill-current">Current</span>
<span class="cnt">4</span>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state" title="Open issue"></span>
<span class="title-text">
<a href="#">Ship grouped Project view rendering</a>
<span class="num">stablyai/orca#1402</span>
</span>
</div>
<div><span class="sel status-inprog">In progress</span></div>
<div><span class="sel p0">P0</span></div>
<div><span class="sel s">S</span></div>
<div><span class="iter">Sprint 42 <span class="dates">Apr 28 – May 12</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar">JL</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state pr" title="Draft PR" style="border-color:var(--draft);background:transparent"></span>
<span class="title-text">
<a href="#">feat(project-view): normalize GraphQL unions</a>
<span class="num">stablyai/orca#1411</span>
</span>
</div>
<div><span class="sel status-rev">In review</span></div>
<div><span class="sel p0">P0</span></div>
<div><span class="sel m">M</span></div>
<div><span class="iter">Sprint 42 <span class="dates">Apr 28 – May 12</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar">JL</span><span class="avatar green">KT</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state" title="Open issue"></span>
<span class="title-text">
<a href="#">Picker: pinned / recent / browse + paste-to-add</a>
<span class="num">stablyai/orca#1415</span>
</span>
</div>
<div><span class="sel status-todo">Todo</span></div>
<div><span class="sel p1">P1</span></div>
<div><span class="sel m">M</span></div>
<div><span class="iter">Sprint 42 <span class="dates">Apr 28 – May 12</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar gray">—</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state" title="Open issue"></span>
<span class="title-text">
<a href="#">Block render until full pagination; 500-item cap</a>
<span class="num">stablyai/orca#1418</span>
</span>
</div>
<div><span class="sel status-todo">Todo</span></div>
<div><span class="sel p2">P2</span></div>
<div><span class="sel xs">XS</span></div>
<div><span class="iter">Sprint 42 <span class="dates">Apr 28 – May 12</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar">JL</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
</div>
<!-- Group: Sprint 43 (future) -->
<div class="group">
<div class="group-header" role="row" tabindex="0">
<span class="chev">▾</span>
<span class="name">Sprint 43</span>
<span class="range">May 13 – May 26</span>
<span class="cnt">2</span>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state" title="Open issue"></span>
<span class="title-text">
<a href="#">Design: view switching (Phase B)</a>
<span class="num">stablyai/orca#1421</span>
</span>
</div>
<div><span class="sel status-todo">Todo</span></div>
<div><span class="sel p1">P1</span></div>
<div><span class="sel l">L</span></div>
<div><span class="iter">Sprint 43 <span class="dates">May 13 – May 26</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar">JL</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
<div class="row" role="row">
<div class="title-cell">
<span class="state closed" title="Closed"></span>
<span class="title-text">
<a href="#">Spike: board-layout vertical grouping feasibility</a>
<span class="num">stablyai/orca#1425</span>
</span>
</div>
<div><span class="sel status-todo">Todo</span></div>
<div><span class="sel p2">P2</span></div>
<div><span class="sel s">S</span></div>
<div><span class="iter">Sprint 43 <span class="dates">May 13 – May 26</span></span></div>
<div class="repo">stablyai/orca</div>
<div style="justify-self:end"><span class="avatars"><span class="avatar red">MF</span></span></div>
<div class="row-actions">
<button title="Open in GitHub">↗</button>
<button title="Start work">▶</button>
</div>
</div>
</div>
<!-- Group: No Sprint -->
<div class="group">
<div class="group-header collapsed" role="row" tabindex="0">
<span class="chev">▾</span>
<span class="name">No Sprint</span>
<span class="cnt">12</span>
</div>
</div>
<div class="note">
<b>Read-only.</b> Rows, columns, grouping, and sort all come from GitHub. To
change the view, edit it on GitHub or pick a different project.
Primary click opens Orca's work-item dialog; hover actions open in GitHub or start a worktree.
</div>
</div>
</div><!-- /.gh-panel-body -->
</div><!-- /.gh-panel -->
<!-- Secondary mockups ----------------------------------------------- -->
<div class="section-title">Too-large state (totalCount &gt; 500)</div>
<div class="state-panel">
<div class="head">This view has 873 items — too large to render in Orca.</div>
<div>Narrow the view's filter on GitHub, then retry.</div>
<div class="actions">
<button class="btn primary">Open in GitHub</button>
<button class="btn">Pick another project</button>
</div>
</div>
<div class="section-title">Unsupported layout (Board / Roadmap)</div>
<div class="state-panel">
<div class="head">Orca only renders table views yet.</div>
<div>This is a <b>Board</b> view. Switch to a table view or open in GitHub.</div>
<div class="actions">
<button class="btn primary">Open in GitHub</button>
</div>
</div>
<div class="section-title">Auth / scope error (banner above list, cached rows remain)</div>
<div class="banner">
<span>GitHub project access needs the <code class="kbd">project</code> scope. Run <span class="mono">gh auth refresh -s project</span>.</span>
<span class="grow"></span>
<button class="btn">Copy command</button>
</div>
</div>
<!-- Picker popover (click project picker button to toggle) -->
<div class="overlay" id="overlay" role="dialog" aria-label="Choose a project">
<div class="popover">
<div class="search"><input placeholder="Search projects by title, owner, or number…" /></div>
<div class="sec-label">Pinned</div>
<div class="pitem current">
<span>📌</span>
<span class="title">Stably Product Board</span>
<span class="meta">stablyai / 3</span>
</div>
<div class="pitem">
<span>📌</span>
<span class="title">Infra Roadmap</span>
<span class="meta">stablyai / 7</span>
</div>
<div class="pitem">
<span>📌</span>
<span class="title" style="color:var(--fg-muted)">Archived Beta Launch</span>
<span class="warn">⚠ no access</span>
<span class="meta" style="margin-left:8px;color:var(--accent);cursor:pointer">Remove pin</span>
</div>
<div class="sec-label">Recent</div>
<div class="pitem">
<span>🕘</span>
<span class="title">Growth Experiments</span>
<span class="meta">stablyai / 12</span>
</div>
<div class="pitem">
<span>🕘</span>
<span class="title">Personal Tasks</span>
<span class="meta">jinjing / 1</span>
</div>
<div class="sec-label">Browse</div>
<div class="pitem">
<span>📁</span>
<span class="title">Docs Overhaul</span>
<span class="meta">stablyai / 9</span>
</div>
<div class="pitem">
<span>📁</span>
<span class="title">Hiring Pipeline</span>
<span class="meta">stablyai / 4</span>
</div>
<div class="paste">
<div class="sec-label" style="padding:0 0 4px">Add by URL or owner/number</div>
<input placeholder="https://github.com/orgs/stablyai/projects/3 or stablyai/3" />
</div>
</div>
</div>
<script>
const overlay = document.getElementById('overlay');
document.getElementById('pickerBtn').addEventListener('click', () => {
overlay.classList.toggle('open');
});
overlay.addEventListener('click', (e) => {
if (e.target === overlay) overlay.classList.remove('open');
});
document.querySelectorAll('.group-header').forEach(h => {
h.addEventListener('click', () => h.classList.toggle('collapsed'));
});
</script>
</body>
</html>