@pa1nd/horse-browser 0.4.0
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/LICENSE +21 -0
- package/README.md +136 -0
- package/SKILL.md +96 -0
- package/agent-helpers.py +215 -0
- package/bin/horse-browser +419 -0
- package/claude-md.sh +103 -0
- package/extension/background.js +139 -0
- package/extension/hello.html +233 -0
- package/extension/hello.js +12 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +14 -0
- package/extension/monitor.css +234 -0
- package/extension/monitor.html +53 -0
- package/extension/monitor.js +541 -0
- package/install.sh +178 -0
- package/package.json +53 -0
- package/scripts/postinstall.sh +34 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>🐴 Horse Browser</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #202124; --line: #3c4043; --line-soft: #2a2b2e;
|
|
10
|
+
--text: #e8eaed; --dim: #9aa0a6; --faint: #5f6368;
|
|
11
|
+
--accent: #8ab4f8; --brand: #34a06b;
|
|
12
|
+
--font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
13
|
+
}
|
|
14
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
15
|
+
html { height: 100%; }
|
|
16
|
+
body {
|
|
17
|
+
min-height: 100%;
|
|
18
|
+
background-color: var(--bg);
|
|
19
|
+
background-image: radial-gradient(1300px 680px at 50% -40px, #23311f 0%, transparent 60%);
|
|
20
|
+
background-repeat: no-repeat;
|
|
21
|
+
color: var(--text); font-family: var(--font); line-height: 1.6;
|
|
22
|
+
padding: 70px 28px 80px;
|
|
23
|
+
}
|
|
24
|
+
.wrap { width: 100%; max-width: 900px; margin: 0 auto; }
|
|
25
|
+
|
|
26
|
+
/* ── hero / big headline ── */
|
|
27
|
+
.hero { text-align: center; }
|
|
28
|
+
.horse { font-size: 66px; line-height: 1; filter: drop-shadow(0 8px 22px rgba(52,160,107,.45)); }
|
|
29
|
+
.eyebrow { text-transform: uppercase; letter-spacing: .18em; font-size: 11.5px; font-weight: 600; color: var(--faint); }
|
|
30
|
+
.hero .eyebrow { margin-top: 20px; }
|
|
31
|
+
h1 { font-size: 46px; font-weight: 680; letter-spacing: -.026em; line-height: 1.06; margin: 15px auto 18px; max-width: 15ch; }
|
|
32
|
+
.lead { color: var(--dim); font-size: 17px; max-width: 56ch; margin: 0 auto; }
|
|
33
|
+
|
|
34
|
+
/* ── sections ── */
|
|
35
|
+
.sec { padding: 62px 0; border-top: 1px solid var(--line-soft); }
|
|
36
|
+
.sec:first-of-type { margin-top: 58px; }
|
|
37
|
+
.sec h2 { font-size: 27px; font-weight: 650; letter-spacing: -.014em; margin-bottom: 13px; }
|
|
38
|
+
.sec p { color: var(--dim); font-size: 15.5px; }
|
|
39
|
+
.sec b { color: var(--text); font-weight: 600; }
|
|
40
|
+
|
|
41
|
+
.grid2 { display: grid; grid-template-columns: 1fr 282px; gap: 50px; align-items: center; }
|
|
42
|
+
.grid2 p { max-width: 42ch; }
|
|
43
|
+
.center { text-align: center; }
|
|
44
|
+
.center p { max-width: 58ch; margin: 0 auto; }
|
|
45
|
+
.center .ico-lg { font-size: 34px; line-height: 1; margin-bottom: 10px; }
|
|
46
|
+
|
|
47
|
+
.cta { display: inline-block; margin-top: 26px; padding: 9px 18px; border-radius: 10px;
|
|
48
|
+
background: var(--brand); color: #fff; font-size: 14px; font-weight: 600; border: 0; cursor: pointer; }
|
|
49
|
+
.cta:hover { filter: brightness(1.1); }
|
|
50
|
+
.foot { text-align: center; color: var(--faint); font-size: 12.5px; margin-top: 62px; }
|
|
51
|
+
.foot .brand { color: var(--brand); font-weight: 600; }
|
|
52
|
+
|
|
53
|
+
@media (max-width: 720px) {
|
|
54
|
+
h1 { font-size: 34px; }
|
|
55
|
+
.grid2 { grid-template-columns: 1fr; gap: 30px; }
|
|
56
|
+
.fb.big { height: 380px; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ── fake-browser pieces (ported from the atelier "Claude 5IQ" page) ── */
|
|
60
|
+
.fb { display: flex; height: 372px; border: 1px solid rgba(255,255,255,.10); border-radius: 12px;
|
|
61
|
+
overflow: hidden; box-shadow: 0 26px 60px -28px rgba(0,0,0,.7); font-size: 11px; text-align: left; }
|
|
62
|
+
.fb.big { height: 472px; margin-top: 32px; }
|
|
63
|
+
.fb-side { width: 196px; flex: 0 0 auto; display: flex; flex-direction: column;
|
|
64
|
+
background: #161618; border-right: 1px solid rgba(0,0,0,.4); }
|
|
65
|
+
.fb-side.solo { width: 100%; height: 372px; border: 1px solid rgba(255,255,255,.10); border-radius: 12px;
|
|
66
|
+
box-shadow: 0 26px 60px -28px rgba(0,0,0,.7); font-size: 11px; text-align: left; }
|
|
67
|
+
.fb-stop { display: flex; align-items: center; gap: 10px; padding: 11px 11px 7px; }
|
|
68
|
+
.fb-dots { display: flex; gap: 5px; }
|
|
69
|
+
.fb-dots i { width: 9px; height: 9px; border-radius: 50%; background: #52525b; }
|
|
70
|
+
.fb-ic { width: 15px; height: 15px; color: #a1a1aa; }
|
|
71
|
+
.fb-switch { margin-left: auto; display: flex; gap: 2px; background: rgba(255,255,255,.04); border-radius: 7px; padding: 2px; }
|
|
72
|
+
.fb-switch span { width: 19px; height: 19px; display: grid; place-items: center; border-radius: 5px; color: #a1a1aa; }
|
|
73
|
+
.fb-switch span.on { background: rgba(255,255,255,.09); color: #f4f4f5; }
|
|
74
|
+
.fb-switch svg { width: 12px; height: 12px; }
|
|
75
|
+
.fb-sbody { flex: 1; overflow: hidden; padding: 0 9px 9px; }
|
|
76
|
+
.fb-logo { display: flex; justify-content: center; background: rgba(255,255,255,.05); border-radius: 8px; padding: 7px 0; margin-bottom: 11px; }
|
|
77
|
+
.fb-logo span { width: 24px; height: 24px; display: grid; place-items: center; border-radius: 6px; background: #3a7d4a; font-size: 13px; }
|
|
78
|
+
.fb-group { margin-bottom: 8px; }
|
|
79
|
+
.fb-ghead { display: flex; align-items: center; gap: 6px; border-radius: 8px; padding: 6px 9px; color: #1f1813; font-weight: 600; }
|
|
80
|
+
.fb-ghead .fb-code { font-variant-numeric: tabular-nums; letter-spacing: .04em; }
|
|
81
|
+
.fb-ghead svg { width: 12px; height: 12px; margin-left: auto; opacity: .55; }
|
|
82
|
+
.fb-tabs { margin-top: 4px; padding: 2px 0 2px 9px; }
|
|
83
|
+
.fb-tab { display: flex; align-items: center; gap: 8px; border-radius: 6px; padding: 4px 6px; color: #d4d4d8; }
|
|
84
|
+
.fb-tab .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
85
|
+
.fb-fav { width: 15px; height: 15px; flex: 0 0 auto; display: grid; place-items: center; border-radius: 3px; font-size: 9px; font-weight: 700; line-height: 1; }
|
|
86
|
+
.fb-fav.emoji { background: transparent; font-size: 12px; }
|
|
87
|
+
.fb-fav.bbc { background: #000; color: #fff; font-size: 6px; font-weight: 900; letter-spacing: -.4px; }
|
|
88
|
+
.fb-fav.atelier { grid-template-columns: 1fr 1fr; place-items: stretch; gap: 1px; padding: 2.5px; background: rgba(255,255,255,.08); }
|
|
89
|
+
.fb-fav.atelier i { border-radius: 1px; }
|
|
90
|
+
.fb-plus { display: flex; justify-content: center; background: rgba(255,255,255,.05); border-radius: 8px; padding: 6px 0; color: #a1a1aa; margin-top: 7px; font-size: 14px; line-height: 1; }
|
|
91
|
+
.fb-main { flex: 1; display: flex; flex-direction: column; background: #252528; min-width: 0; }
|
|
92
|
+
.fb-mtop { display: flex; align-items: center; gap: 9px; padding: 11px 11px 8px; }
|
|
93
|
+
.fb-nav { display: flex; align-items: center; gap: 6px; color: #a1a1aa; }
|
|
94
|
+
.fb-nav svg { width: 15px; height: 15px; }
|
|
95
|
+
.fb-nav .dim { color: #52525b; }
|
|
96
|
+
.fb-addr { flex: 1; min-width: 0; display: flex; align-items: center; gap: 7px; background: rgba(255,255,255,.07);
|
|
97
|
+
border-radius: 999px; padding: 6px 12px; color: #f4f4f5; font-weight: 500; }
|
|
98
|
+
.fb-lock { width: 13px; height: 13px; flex: 0 0 auto; border: 1.5px solid rgba(212,212,216,.85); border-radius: 4px; }
|
|
99
|
+
.fb-url { color: #71717a; font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
100
|
+
.fb-wall { flex: 1; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 3px;
|
|
101
|
+
background: rgba(0,0,0,.4); padding: 3px; position: relative; }
|
|
102
|
+
.fb-cell { border-radius: 4px; background: #0d0d0f; position: relative; overflow: hidden; }
|
|
103
|
+
.fb-cell.live { background: linear-gradient(160deg, #1c1f25, #0d0d0f 72%); }
|
|
104
|
+
.fb-badge { position: absolute; left: 6px; top: 6px; display: inline-flex; align-items: center; gap: 4px;
|
|
105
|
+
background: rgba(0,0,0,.55); border-radius: 4px; padding: 1px 6px; font-size: 8.5px; font-weight: 600; color: #fff; }
|
|
106
|
+
.fb-badge i, .fb-live i { width: 5px; height: 5px; border-radius: 50%; background: #34d399; flex: 0 0 auto; }
|
|
107
|
+
.fb-empty { position: absolute; inset: 0; display: grid; place-items: center; color: #52525b; font-size: 9.5px; text-align: center; padding: 6px; }
|
|
108
|
+
.fb-live { position: absolute; right: 8px; bottom: 8px; display: inline-flex; align-items: center; gap: 5px;
|
|
109
|
+
background: rgba(0,0,0,.6); border-radius: 999px; padding: 3px 10px; font-size: 9.5px; color: #e4e4e7; }
|
|
110
|
+
</style>
|
|
111
|
+
</head>
|
|
112
|
+
<body>
|
|
113
|
+
<div class="wrap">
|
|
114
|
+
<header class="hero">
|
|
115
|
+
<div class="horse">🐴</div>
|
|
116
|
+
<p class="eyebrow">Welcome to Horse Browser</p>
|
|
117
|
+
<h1>It's for your agents.</h1>
|
|
118
|
+
<p class="lead">A dedicated Chrome your agents drive over CDP — separate from your daily browser, so they never collide with your work.</p>
|
|
119
|
+
</header>
|
|
120
|
+
|
|
121
|
+
<section class="sec grid2">
|
|
122
|
+
<div>
|
|
123
|
+
<h2>They open & close tabs</h2>
|
|
124
|
+
<p>Each agent session lands in its own coloured tab group, so a glance tells you which agent opened what. Tabs open quietly in the background — they never steal your focus.</p>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="fb-side solo">
|
|
127
|
+
<div class="fb-stop">
|
|
128
|
+
<span class="fb-dots"><i></i><i></i><i></i></span>
|
|
129
|
+
<svg class="fb-ic" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="1.5" y="2.5" width="13" height="11" rx="2"/><line x1="6" y1="2.5" x2="6" y2="13.5"/></svg>
|
|
130
|
+
<span class="fb-switch">
|
|
131
|
+
<span class="on"><svg viewBox="0 0 16 16" fill="currentColor"><rect x="2" y="2" width="5" height="5" rx="1"/><rect x="9" y="2" width="5" height="5" rx="1"/><rect x="2" y="9" width="5" height="5" rx="1"/><rect x="9" y="9" width="5" height="5" rx="1"/></svg></span>
|
|
132
|
+
<span><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="7" cy="7" r="4.3"/><line x1="10.4" y1="10.4" x2="14" y2="14"/></svg></span>
|
|
133
|
+
</span>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="fb-sbody">
|
|
136
|
+
<div class="fb-logo"><span>🐴</span></div>
|
|
137
|
+
<div class="fb-group">
|
|
138
|
+
<div class="fb-ghead" style="background:#f0b487"><span>🐯</span><span class="fb-code">9C52</span>
|
|
139
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l4-4 4 4"/></svg></div>
|
|
140
|
+
<div class="fb-tabs" style="box-shadow:inset 2px 0 0 #e07b2f">
|
|
141
|
+
<div class="fb-tab"><span class="fb-fav atelier"><i style="background:#a1a1aa"></i><i style="background:#3b82f6"></i><i style="background:#71717a"></i><i style="background:#a1a1aa"></i></span><span class="t">Atelier · Claude 5IQ</span></div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="fb-group">
|
|
145
|
+
<div class="fb-ghead" style="background:#a8d6a0"><span>🐢</span><span class="fb-code">C366</span>
|
|
146
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l4-4 4 4"/></svg></div>
|
|
147
|
+
<div class="fb-tabs" style="box-shadow:inset 2px 0 0 #46985a">
|
|
148
|
+
<div class="fb-tab"><span class="fb-fav emoji">🌐</span><span class="t">Example Domain</span></div>
|
|
149
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#fff;color:#111;font-family:Georgia,serif">W</span><span class="t">Wikipedia, the free encyclop…</span></div>
|
|
150
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#ff6600;color:#fff">Y</span><span class="t">Hacker News</span></div>
|
|
151
|
+
<div class="fb-tab"><span class="fb-fav emoji">🐙</span><span class="t">GitHub · Change is constant…</span></div>
|
|
152
|
+
<div class="fb-tab"><span class="fb-fav emoji">🍎</span><span class="t">Apple</span></div>
|
|
153
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#5100ff;color:#fff">▽</span><span class="t">The Verge</span></div>
|
|
154
|
+
<div class="fb-tab"><span class="fb-fav bbc">BBC</span><span class="t">BBC News – Breaking news,</span></div>
|
|
155
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#000;color:#fff">M</span><span class="t">MDN Web Docs</span></div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="fb-plus">+</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
|
|
163
|
+
<section class="sec center">
|
|
164
|
+
<div class="ico-lg">🔑</div>
|
|
165
|
+
<h2>It's just Chrome</h2>
|
|
166
|
+
<p>Sign into Gmail, GitHub, your dashboards <b>once</b> — every agent inherits those logins. Install any extension, change any setting. It's a normal Chrome profile, yours to set up however helps your agents.</p>
|
|
167
|
+
</section>
|
|
168
|
+
|
|
169
|
+
<section class="sec center">
|
|
170
|
+
<h2>Watch them — the Agent Monitor</h2>
|
|
171
|
+
<p>A live wall of every agent's tabs on stable tiles — watch them all at once, no reshuffling. Click the 🐴 toolbar button anytime; the Monitor stays pinned as your first tab.</p>
|
|
172
|
+
<div class="fb big">
|
|
173
|
+
<div class="fb-side">
|
|
174
|
+
<div class="fb-stop">
|
|
175
|
+
<span class="fb-dots"><i></i><i></i><i></i></span>
|
|
176
|
+
<svg class="fb-ic" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="1.5" y="2.5" width="13" height="11" rx="2"/><line x1="6" y1="2.5" x2="6" y2="13.5"/></svg>
|
|
177
|
+
<span class="fb-switch">
|
|
178
|
+
<span class="on"><svg viewBox="0 0 16 16" fill="currentColor"><rect x="2" y="2" width="5" height="5" rx="1"/><rect x="9" y="2" width="5" height="5" rx="1"/><rect x="2" y="9" width="5" height="5" rx="1"/><rect x="9" y="9" width="5" height="5" rx="1"/></svg></span>
|
|
179
|
+
<span><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="7" cy="7" r="4.3"/><line x1="10.4" y1="10.4" x2="14" y2="14"/></svg></span>
|
|
180
|
+
</span>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="fb-sbody">
|
|
183
|
+
<div class="fb-logo"><span>🐴</span></div>
|
|
184
|
+
<div class="fb-group">
|
|
185
|
+
<div class="fb-ghead" style="background:#f0b487"><span>🐯</span><span class="fb-code">9C52</span>
|
|
186
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l4-4 4 4"/></svg></div>
|
|
187
|
+
<div class="fb-tabs" style="box-shadow:inset 2px 0 0 #e07b2f">
|
|
188
|
+
<div class="fb-tab"><span class="fb-fav atelier"><i style="background:#a1a1aa"></i><i style="background:#3b82f6"></i><i style="background:#71717a"></i><i style="background:#a1a1aa"></i></span><span class="t">Atelier · Claude 5IQ</span></div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="fb-group">
|
|
192
|
+
<div class="fb-ghead" style="background:#a8d6a0"><span>🐢</span><span class="fb-code">C366</span>
|
|
193
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l4-4 4 4"/></svg></div>
|
|
194
|
+
<div class="fb-tabs" style="box-shadow:inset 2px 0 0 #46985a">
|
|
195
|
+
<div class="fb-tab"><span class="fb-fav emoji">🌐</span><span class="t">Example Domain</span></div>
|
|
196
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#fff;color:#111;font-family:Georgia,serif">W</span><span class="t">Wikipedia, the free encyclop…</span></div>
|
|
197
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#ff6600;color:#fff">Y</span><span class="t">Hacker News</span></div>
|
|
198
|
+
<div class="fb-tab"><span class="fb-fav emoji">🐙</span><span class="t">GitHub · Change is constant…</span></div>
|
|
199
|
+
<div class="fb-tab"><span class="fb-fav emoji">🍎</span><span class="t">Apple</span></div>
|
|
200
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#5100ff;color:#fff">▽</span><span class="t">The Verge</span></div>
|
|
201
|
+
<div class="fb-tab"><span class="fb-fav bbc">BBC</span><span class="t">BBC News – Breaking news,</span></div>
|
|
202
|
+
<div class="fb-tab"><span class="fb-fav" style="background:#000;color:#fff">M</span><span class="t">MDN Web Docs</span></div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="fb-plus">+</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="fb-main">
|
|
209
|
+
<div class="fb-mtop">
|
|
210
|
+
<span class="fb-nav">
|
|
211
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 3.5L5 8l4.5 4.5"/><path d="M5 8h8"/></svg>
|
|
212
|
+
<svg class="dim" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 3.5L11 8l-4.5 4.5"/><path d="M11 8H3"/></svg>
|
|
213
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8a5 5 0 1 1-1.4-3.5"/><path d="M13 2.5V6h-3.5"/></svg>
|
|
214
|
+
</span>
|
|
215
|
+
<span class="fb-addr"><span class="fb-lock"></span>Agent Tab Grouper<span class="fb-url">· monitor.html</span></span>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="fb-wall">
|
|
218
|
+
<div class="fb-cell live"><span class="fb-badge"><i></i>1</span></div>
|
|
219
|
+
<div class="fb-cell"><span class="fb-empty">waiting for an agent</span></div>
|
|
220
|
+
<div class="fb-cell live"><span class="fb-badge"><i></i>3</span></div>
|
|
221
|
+
<div class="fb-cell"><span class="fb-empty">waiting for an agent</span></div>
|
|
222
|
+
<span class="fb-live"><i></i>2 agents browsing</span>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
<button id="open-monitor" class="cta">Open the Monitor →</button>
|
|
227
|
+
</section>
|
|
228
|
+
|
|
229
|
+
<p class="foot">Close this tab whenever — your agents are ready. <span class="brand">🐴 Horse Browser</span></p>
|
|
230
|
+
</div>
|
|
231
|
+
<script src="hello.js"></script>
|
|
232
|
+
</body>
|
|
233
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// "Open the Monitor →" — focus the pinned Monitor tab if it's there, else open it pinned.
|
|
2
|
+
// External file (not inline): MV3 extension pages block inline <script> under the default CSP.
|
|
3
|
+
document.getElementById("open-monitor").addEventListener("click", async () => {
|
|
4
|
+
const url = chrome.runtime.getURL("monitor.html");
|
|
5
|
+
const tabs = await chrome.tabs.query({ url });
|
|
6
|
+
if (tabs.length) {
|
|
7
|
+
await chrome.tabs.update(tabs[0].id, { active: true });
|
|
8
|
+
await chrome.windows.update(tabs[0].windowId, { focused: true });
|
|
9
|
+
} else {
|
|
10
|
+
await chrome.tabs.create({ url, pinned: true, index: 0 });
|
|
11
|
+
}
|
|
12
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "Agent Tab Grouper",
|
|
4
|
+
"version": "0.5.12",
|
|
5
|
+
"description": "Groups CDP-driven automation tabs into a per-session tab group, and serves the Agent Monitor — a live CCTV grid of all sessions' tabs. Driven over CDP via Runtime.evaluate against this extension's service worker.",
|
|
6
|
+
"icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" },
|
|
7
|
+
"background": { "service_worker": "background.js" },
|
|
8
|
+
"action": {
|
|
9
|
+
"default_title": "Open Agent Monitor",
|
|
10
|
+
"default_icon": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" }
|
|
11
|
+
},
|
|
12
|
+
"permissions": ["tabs", "tabGroups", "debugger", "alarms"],
|
|
13
|
+
"host_permissions": ["http://127.0.0.1:9223/*"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/* Horse Browser monitor — styled to feel like Chrome: Material "dark mode" greys
|
|
2
|
+
+ Google-grey text, Chrome blue/green accents, system UI font. The horse logo
|
|
3
|
+
keeps its emerald mark; per-session bars use Chrome's own tab-group colours. */
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--bg: #202124; /* Chrome dark base (the wall) */
|
|
7
|
+
--panel: #292a2d; /* side-panel surface */
|
|
8
|
+
--card: #303134; /* list row */
|
|
9
|
+
--card-hi: #3c4043; /* hover / elevated */
|
|
10
|
+
--line: #3c4043; /* dividers */
|
|
11
|
+
--line-soft: #303134;
|
|
12
|
+
--text: #e8eaed; /* Google grey 100 */
|
|
13
|
+
--dim: #9aa0a6; /* Google grey 500 */
|
|
14
|
+
--faint: #5f6368; /* Google grey 600 */
|
|
15
|
+
--accent: #8ab4f8; /* Chrome dark-mode blue */
|
|
16
|
+
--accent-br: #81c995; /* Chrome green — "live / active" */
|
|
17
|
+
--brand: #2f855a; /* horse-icon emerald (logo only) */
|
|
18
|
+
--font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* The sidebar follows the BROWSER theme (prefers-color-scheme) like Chrome's own
|
|
22
|
+
chrome: light tab-strip in light mode, dark panel in dark mode. The wall stays
|
|
23
|
+
dark either way. Re-declaring the tokens on #sidebar re-themes every descendant.
|
|
24
|
+
Default below = light (covers light + no-preference); dark mode overrides it. */
|
|
25
|
+
#sidebar {
|
|
26
|
+
--panel: #ffffff; /* side-panel surface (Chrome light) */
|
|
27
|
+
--card: #f1f3f4; /* list row (Google grey 100) */
|
|
28
|
+
--card-hi: #e8eaed; /* hover / elevated */
|
|
29
|
+
--line: #dadce0; /* divider (Google grey 300) */
|
|
30
|
+
--line-soft: #e8eaed;
|
|
31
|
+
--text: #202124; /* Google grey 900 */
|
|
32
|
+
--dim: #5f6368; /* Google grey 700 */
|
|
33
|
+
--faint: #80868b; /* Google grey 600 */
|
|
34
|
+
--accent-br: #1e8e3e; /* Chrome light-mode green — LIVE / active */
|
|
35
|
+
--pill: #e0e3e7; /* slot-number pill bg (idle), visible on the light card */
|
|
36
|
+
}
|
|
37
|
+
@media (prefers-color-scheme: dark) {
|
|
38
|
+
#sidebar {
|
|
39
|
+
--panel: #292a2d;
|
|
40
|
+
--card: #35363a;
|
|
41
|
+
--card-hi: #3c4043;
|
|
42
|
+
--line: #3c4043;
|
|
43
|
+
--line-soft: #303134;
|
|
44
|
+
--text: #e8eaed;
|
|
45
|
+
--dim: #9aa0a6;
|
|
46
|
+
--faint: #80868b;
|
|
47
|
+
--accent-br: #81c995;
|
|
48
|
+
--pill: #4a4d51;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
* { box-sizing: border-box; }
|
|
53
|
+
html, body { margin: 0; height: 100%; overflow: hidden; background: var(--bg); color: var(--text);
|
|
54
|
+
font: 13px/1.5 var(--font); -webkit-font-smoothing: antialiased; }
|
|
55
|
+
body { display: flex; flex-direction: row; }
|
|
56
|
+
button { font: inherit; color: inherit; }
|
|
57
|
+
|
|
58
|
+
/* ── sidebar ──────────────────────────────────────────────────────────── */
|
|
59
|
+
#sidebar { flex: 0 0 248px; width: 248px; display: flex; flex-direction: column;
|
|
60
|
+
background: var(--panel); border-right: 1px solid var(--line-soft); overflow: hidden;
|
|
61
|
+
transition: flex-basis .3s cubic-bezier(.4, 0, .2, 1), width .3s cubic-bezier(.4, 0, .2, 1); }
|
|
62
|
+
body.collapsed #sidebar { flex-basis: 60px; width: 60px; }
|
|
63
|
+
/* fully hidden: the panel is removed entirely. Its collapsed/expanded width is
|
|
64
|
+
kept (only the class differs), so revealing restores that state. The floating
|
|
65
|
+
horse is the only thing left on screen. */
|
|
66
|
+
body.sb-hidden #sidebar { flex-basis: 0; width: 0; border-right-width: 0; }
|
|
67
|
+
|
|
68
|
+
.sb-head { display: flex; align-items: center; gap: 9px; padding: 13px 12px 11px;
|
|
69
|
+
border-bottom: 1px solid var(--line-soft); transition: padding .3s cubic-bezier(.4, 0, .2, 1); }
|
|
70
|
+
/* centre the 22px logo in the 60px rail — glides via the padding transition */
|
|
71
|
+
body.collapsed .sb-head { padding-left: 19px; }
|
|
72
|
+
.brand { display: flex; align-items: center; gap: 9px; min-width: 0; flex: 1; }
|
|
73
|
+
.logo { width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center;
|
|
74
|
+
background: var(--brand); font-size: 15px; line-height: 1; flex: 0 0 auto; cursor: pointer;
|
|
75
|
+
box-shadow: 0 1px 3px rgba(0,0,0,.2); }
|
|
76
|
+
.word { font-weight: 500; letter-spacing: 0; font-size: 14px; color: var(--text);
|
|
77
|
+
white-space: nowrap; transition: opacity .16s ease; }
|
|
78
|
+
body.collapsed .word { opacity: 0; }
|
|
79
|
+
|
|
80
|
+
.icon-btn { background: transparent; border: 1px solid transparent; border-radius: 6px;
|
|
81
|
+
width: 24px; height: 24px; display: grid; place-items: center; cursor: pointer; color: var(--dim);
|
|
82
|
+
flex: 0 0 auto; transition: background .12s, color .12s, border-color .12s, opacity .16s ease; }
|
|
83
|
+
.icon-btn:hover { background: var(--card-hi); color: var(--text); border-color: var(--line); }
|
|
84
|
+
.icon-btn svg { width: 14px; height: 14px; display: block; }
|
|
85
|
+
/* collapse keeps the header a row so the logo stays put; the buttons fade */
|
|
86
|
+
body.collapsed #collapse, body.collapsed #hide { opacity: 0; pointer-events: none; }
|
|
87
|
+
|
|
88
|
+
/* "?" help button — lives inline in the footer bar; opens the welcome page on click */
|
|
89
|
+
#help { font-size: 13px; font-weight: 700; color: var(--faint); }
|
|
90
|
+
|
|
91
|
+
.spacer { flex: 1; }
|
|
92
|
+
.live { display: inline-flex; align-items: center; gap: 6px; letter-spacing: .04em;
|
|
93
|
+
font-size: 11px; color: var(--accent-br); }
|
|
94
|
+
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent-br);
|
|
95
|
+
animation: pulse 2.2s infinite; }
|
|
96
|
+
.count { display: inline-flex; align-items: baseline; }
|
|
97
|
+
.stat { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
98
|
+
.stat-u { color: var(--faint); font-size: 11px; margin-left: 4px; }
|
|
99
|
+
|
|
100
|
+
.tablist { flex: 1; min-height: 0; overflow-y: auto; padding: 8px; }
|
|
101
|
+
/* Collapsed hides the footer, so the list runs to the window's bottom edge and
|
|
102
|
+
the last card hard-clips there into a rounded half-box (the "artifact"). Fade
|
|
103
|
+
the bottom ~one card height so a clipped card dissolves cleanly. Collapsed-only
|
|
104
|
+
— expanded mode is capped by the footer and needs no mask. */
|
|
105
|
+
body.collapsed .tablist {
|
|
106
|
+
-webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 56px), transparent);
|
|
107
|
+
mask-image: linear-gradient(to bottom, #000 calc(100% - 56px), transparent); }
|
|
108
|
+
.tablist::-webkit-scrollbar { width: 8px; }
|
|
109
|
+
.tablist::-webkit-scrollbar-track { background: transparent; }
|
|
110
|
+
.tablist::-webkit-scrollbar-thumb { background: rgba(0,0,0,.18); border-radius: 4px;
|
|
111
|
+
border: 2px solid transparent; background-clip: content-box; }
|
|
112
|
+
.tablist:hover::-webkit-scrollbar-thumb { background: rgba(0,0,0,.30); background-clip: content-box; }
|
|
113
|
+
/* the collapsed rail is too narrow for a scrollbar — it reads as a stray bar */
|
|
114
|
+
body.collapsed .tablist { scrollbar-width: none; }
|
|
115
|
+
body.collapsed .tablist::-webkit-scrollbar { width: 0; height: 0; }
|
|
116
|
+
|
|
117
|
+
/* ── session groups: Chrome-style coloured groups (emoji + code header, a coloured
|
|
118
|
+
spine, favicon rows) — mirrors the welcome-page demo. ── */
|
|
119
|
+
.sb-group { margin-bottom: 5px; }
|
|
120
|
+
.sb-ghead { display: flex; align-items: center; gap: 6px; border-radius: 8px; padding: 4px 10px;
|
|
121
|
+
color: #1f1813; font-weight: 600; font-size: 12.5px; cursor: default; overflow: hidden; transition: max-height .2s ease, padding .2s ease; }
|
|
122
|
+
.sb-gname { font-variant-numeric: tabular-nums; letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
123
|
+
.sb-chev { width: 13px; height: 13px; margin-left: auto; opacity: .5; flex: 0 0 auto; }
|
|
124
|
+
.sb-group.ungrouped .sb-ghead { display: none; }
|
|
125
|
+
.sb-gtabs { margin-top: 2px; padding-left: 11px; }
|
|
126
|
+
|
|
127
|
+
/* tab row — favicon + title, with a colour-dot favicon fallback and an "active" dot */
|
|
128
|
+
.tab { --c: var(--faint); position: relative; display: flex; align-items: center; gap: 9px;
|
|
129
|
+
padding: 3px 8px; margin-top: 1px; border-radius: 7px; cursor: default; animation: cardIn .24s ease both; }
|
|
130
|
+
.tab.active { background: var(--card-hi); }
|
|
131
|
+
.tab-ico { position: relative; width: 16px; height: 16px; flex: 0 0 auto; display: grid; place-items: center; }
|
|
132
|
+
.tab-ico img { width: 16px; height: 16px; border-radius: 3px; display: none; }
|
|
133
|
+
.tab-ico.has-ico img { display: block; }
|
|
134
|
+
.tab-ico::after { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--c); }
|
|
135
|
+
.tab-ico.has-ico::after { display: none; }
|
|
136
|
+
.tab-title { flex: 1; min-width: 0; color: var(--text); font-weight: 400; font-size: 13px;
|
|
137
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity .16s ease; }
|
|
138
|
+
.tab-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-br); flex: 0 0 auto;
|
|
139
|
+
opacity: 0; transition: opacity .16s; }
|
|
140
|
+
.tab.active .tab-dot { opacity: 1; }
|
|
141
|
+
|
|
142
|
+
/* slot number — which wall cell this tab is in (1-based, matches the pane badge). Shown only
|
|
143
|
+
for tabs on the wall; the numbered pill replaces the plain dot there, and turns green when
|
|
144
|
+
the tab is active — so a row and its preview always carry the same number. */
|
|
145
|
+
.slot-no { width: 17px; height: 17px; flex: 0 0 auto; display: none; align-items: center; justify-content: center;
|
|
146
|
+
border-radius: 5px; font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1;
|
|
147
|
+
color: var(--dim); background: var(--card-hi); }
|
|
148
|
+
.tab.on-wall .slot-no { display: flex; }
|
|
149
|
+
.tab.on-wall.active .slot-no { color: #0a2a16; background: var(--accent-br); }
|
|
150
|
+
.tab.on-wall .tab-dot { display: none; }
|
|
151
|
+
|
|
152
|
+
/* collapsed rail: headers fold away and titles/badges fade; favicons + the colour spine stay */
|
|
153
|
+
body.collapsed .sb-ghead { max-height: 0; padding-top: 0; padding-bottom: 0; }
|
|
154
|
+
body.collapsed .sb-gtabs { padding-left: 4px; }
|
|
155
|
+
body.collapsed .tab-title, body.collapsed .tab-dot { opacity: 0; pointer-events: none; }
|
|
156
|
+
body.collapsed .slot-no { display: none; }
|
|
157
|
+
|
|
158
|
+
.sb-foot { display: flex; align-items: center; gap: 8px; padding: 9px 12px;
|
|
159
|
+
border-top: 1px solid var(--line-soft); overflow: hidden; max-height: 60px;
|
|
160
|
+
transition: opacity .16s ease, max-height .3s ease, padding .3s ease, border-color .16s ease; }
|
|
161
|
+
#gridsel { background: var(--card); color: var(--text); border: 1px solid var(--line);
|
|
162
|
+
border-radius: 6px; padding: 3px 6px; font: inherit; font-size: 11px; cursor: pointer; }
|
|
163
|
+
/* collapsed: keep the footer bar — but for the "?" alone. Hide live/count/grid and
|
|
164
|
+
centre the help, so the rail shows just the logo (top) and the "?" (bottom), no clutter. */
|
|
165
|
+
body.collapsed .sb-foot { padding: 9px 0; justify-content: center; gap: 0; border-top-color: transparent; }
|
|
166
|
+
body.collapsed .sb-foot > :not(#help) { display: none; }
|
|
167
|
+
/* the native <select> composites through the OS layer and ignores the footer's
|
|
168
|
+
opacity / overflow / max-height — AND even visibility:hidden — so it bled a
|
|
169
|
+
rounded box into the collapsed corner. display:none is the only reliable kill;
|
|
170
|
+
the select doesn't need to animate (the whole footer fades out anyway). */
|
|
171
|
+
body.collapsed #gridsel { display: none; }
|
|
172
|
+
|
|
173
|
+
/* ── stage / wall ─────────────────────────────────────────────────────── */
|
|
174
|
+
#stage { position: relative; flex: 1; min-width: 0; background: var(--bg); }
|
|
175
|
+
|
|
176
|
+
/* floating horse — shown only when the panel is hidden; click to reveal it.
|
|
177
|
+
Fixed over the bottom-left of the wall, where the panel lives. */
|
|
178
|
+
.reveal { position: fixed; left: 16px; bottom: 16px; z-index: 50;
|
|
179
|
+
width: 40px; height: 40px; padding: 0; border-radius: 11px; display: none;
|
|
180
|
+
place-items: center; cursor: pointer; background: var(--brand); color: #fff;
|
|
181
|
+
font-size: 21px; line-height: 1; border: 1px solid rgba(0,0,0,.25);
|
|
182
|
+
box-shadow: 0 4px 14px rgba(0,0,0,.4); transition: transform .14s ease, box-shadow .14s ease; }
|
|
183
|
+
body.sb-hidden .reveal { display: grid; animation: revealIn .22s ease both; }
|
|
184
|
+
.reveal:hover { transform: translateY(-2px); box-shadow: 0 7px 20px rgba(0,0,0,.5); }
|
|
185
|
+
.reveal:active { transform: translateY(0); }
|
|
186
|
+
@keyframes revealIn { from { opacity: 0; transform: translateY(10px) scale(.9); } to { opacity: 1; transform: none; } }
|
|
187
|
+
|
|
188
|
+
#grid { position: absolute; inset: 0; display: grid; gap: 3px; }
|
|
189
|
+
|
|
190
|
+
.pane { --accent: var(--faint); position: relative; overflow: hidden; min-width: 0; min-height: 0;
|
|
191
|
+
background: #000; cursor: pointer; border-radius: 3px; animation: paneIn .3s ease both; }
|
|
192
|
+
.pane canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
|
|
193
|
+
|
|
194
|
+
/* the slot's "active" outline — the live activity hint. Green = something
|
|
195
|
+
happened on this tab within HOT_MS; it pulses so the eye jumps to the action. */
|
|
196
|
+
.pane.is-active { outline: 2px solid var(--accent-br); outline-offset: -2px;
|
|
197
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-br) 45%, transparent),
|
|
198
|
+
0 0 0 0 color-mix(in srgb, var(--accent-br) 55%, transparent);
|
|
199
|
+
animation: paneGlow 2.2s ease-out infinite; }
|
|
200
|
+
|
|
201
|
+
/* fixed slot number, top-left — mirrors the sidebar row's number so "row 2 = the
|
|
202
|
+
tab in slot 2 on the wall". Subtle; brightens when the slot is active. */
|
|
203
|
+
.slot-badge { box-sizing: border-box; position: absolute; top: 6px; left: 6px; z-index: 2; min-width: 18px; height: 18px;
|
|
204
|
+
padding: 0 4px; border-radius: 5px; display: flex; align-items: center; justify-content: center;
|
|
205
|
+
font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
|
|
206
|
+
color: #e8eaed; background: rgba(0,0,0,.62); border: 1px solid rgba(255,255,255,.5); }
|
|
207
|
+
.pane.is-active .slot-badge { background: var(--accent-br); color: #0a2a16; border-color: rgba(255,255,255,.7); }
|
|
208
|
+
|
|
209
|
+
.empty { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
|
|
210
|
+
justify-content: center; gap: 8px; color: var(--dim); padding: 24px; text-align: center;
|
|
211
|
+
pointer-events: none; }
|
|
212
|
+
.empty[hidden] { display: none; }
|
|
213
|
+
.empty-glyph { font-size: 34px; color: var(--faint); }
|
|
214
|
+
.empty-title { color: var(--text); letter-spacing: 0; font-size: 14px; font-weight: 500; }
|
|
215
|
+
.empty-sub { color: var(--faint); max-width: 320px; font-size: 12px; line-height: 1.6; }
|
|
216
|
+
|
|
217
|
+
/* ── motion ───────────────────────────────────────────────────────────── */
|
|
218
|
+
@keyframes pulse {
|
|
219
|
+
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-br) 55%, transparent); }
|
|
220
|
+
70% { box-shadow: 0 0 0 6px transparent; }
|
|
221
|
+
100% { box-shadow: 0 0 0 0 transparent; }
|
|
222
|
+
}
|
|
223
|
+
@keyframes cardIn { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
|
|
224
|
+
@keyframes paneIn { from { opacity: 0; transform: scale(.985); } to { opacity: 1; transform: none; } }
|
|
225
|
+
/* active-slot halo: keeps the inset ring, pulses an outer glow outward */
|
|
226
|
+
@keyframes paneGlow {
|
|
227
|
+
0% { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-br) 45%, transparent),
|
|
228
|
+
0 0 0 0 color-mix(in srgb, var(--accent-br) 50%, transparent); }
|
|
229
|
+
70% { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-br) 45%, transparent),
|
|
230
|
+
0 0 0 7px transparent; }
|
|
231
|
+
100% { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-br) 45%, transparent),
|
|
232
|
+
0 0 0 0 transparent; }
|
|
233
|
+
}
|
|
234
|
+
@media (prefers-reduced-motion: reduce) { * { animation: none !important; } }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Horse Browser</title>
|
|
7
|
+
<link rel="stylesheet" href="monitor.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<aside id="sidebar">
|
|
11
|
+
<header class="sb-head">
|
|
12
|
+
<div class="brand">
|
|
13
|
+
<span class="logo">🐴</span>
|
|
14
|
+
<span class="word">Horse Browser</span>
|
|
15
|
+
</div>
|
|
16
|
+
<button id="hide" class="icon-btn" title="Hide sidebar (⌘B)" aria-label="Hide sidebar">
|
|
17
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"
|
|
18
|
+
stroke-linecap="round" stroke-linejoin="round"><path d="M8 3.5 3.5 8 8 12.5"/><path d="M12.5 3.5 8 8 12.5 12.5"/></svg>
|
|
19
|
+
</button>
|
|
20
|
+
<button id="collapse" class="icon-btn" title="Collapse sidebar" aria-label="Collapse sidebar">
|
|
21
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"
|
|
22
|
+
stroke-linecap="round" stroke-linejoin="round"><path d="M10 3.5 5.5 8l4.5 4.5"/></svg>
|
|
23
|
+
</button>
|
|
24
|
+
</header>
|
|
25
|
+
|
|
26
|
+
<nav id="tablist" class="tablist"></nav>
|
|
27
|
+
|
|
28
|
+
<footer class="sb-foot">
|
|
29
|
+
<span class="live"><span class="live-dot"></span>LIVE</span>
|
|
30
|
+
<span class="count"><span id="stat-tabs" class="stat">0</span><span class="stat-u">tabs</span></span>
|
|
31
|
+
<span class="spacer"></span>
|
|
32
|
+
<select id="gridsel" title="Grid size">
|
|
33
|
+
<option value="2">2×2</option>
|
|
34
|
+
<option value="3">3×3</option>
|
|
35
|
+
</select>
|
|
36
|
+
<button id="help" class="icon-btn" title="What is Horse Browser?" aria-label="What is Horse Browser?">?</button>
|
|
37
|
+
</footer>
|
|
38
|
+
</aside>
|
|
39
|
+
|
|
40
|
+
<main id="stage">
|
|
41
|
+
<div id="grid"></div>
|
|
42
|
+
<div id="empty" class="empty" hidden>
|
|
43
|
+
<span class="empty-glyph">⌁</span>
|
|
44
|
+
<span class="empty-title">No agent tabs</span>
|
|
45
|
+
<span class="empty-sub">When an agent opens tabs they stream in here live.</span>
|
|
46
|
+
</div>
|
|
47
|
+
</main>
|
|
48
|
+
|
|
49
|
+
<button id="reveal" class="reveal" title="Show sidebar (⌘B)" aria-label="Show sidebar">🐴</button>
|
|
50
|
+
|
|
51
|
+
<script src="monitor.js"></script>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|