@oddessentials/agent-guild 0.25.0 → 0.26.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/README.md +44 -10
- package/bin/agent-guild.mjs +2 -0
- package/package.json +2 -2
- package/src/manager/folder-opener.mjs +96 -0
- package/src/manager/folder-picker.mjs +116 -0
- package/src/manager/folder-picker.ps1 +84 -0
- package/src/manager/github-views.mjs +171 -0
- package/src/manager/github.mjs +56 -1
- package/src/manager/main.mjs +4 -1
- package/src/manager/server.mjs +81 -12
- package/web/activity-favicon.js +71 -0
- package/web/app.js +1136 -94
- package/web/index.html +139 -91
- package/web/layout.js +75 -0
- package/web/repo-search.js +91 -0
- package/web/skins/gnomeland/skin.css +1 -1
- package/web/skins/goblinville/skin.css +2 -2
- package/web/skins/grove/skin.css +1 -1
- package/web/skins/guild/skin.css +3 -3
- package/web/skins/orbital/skin.css +1 -1
- package/web/styles.css +246 -38
package/web/index.html
CHANGED
|
@@ -29,27 +29,34 @@
|
|
|
29
29
|
</div>
|
|
30
30
|
<span id="upgrade-note" class="upgrade-note" hidden></span>
|
|
31
31
|
<button id="upgrade" class="btn upgrade" type="button" hidden></button>
|
|
32
|
-
<button id="
|
|
33
|
-
<div id="
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
<button id="menu-toggle" class="btn menu-toggle" type="button" popovertarget="topbar-menu" aria-label="Menu" title="Menu"></button>
|
|
33
|
+
<div id="topbar-menu" class="topbar-menu menu" popover>
|
|
34
|
+
<button id="github-toggle" class="btn github-toggle" type="button" aria-controls="dock" aria-pressed="false">GitHub</button>
|
|
35
|
+
<button id="notes-open" class="btn notes-open" type="button" aria-controls="dock" aria-pressed="false">Notes</button>
|
|
36
|
+
<button id="settings" class="btn settings menu-button" type="button" popovertarget="settings-menu" aria-haspopup="dialog">Settings</button>
|
|
37
|
+
<div id="settings-menu" class="settings-menu menu" popover role="dialog" aria-label="Settings">
|
|
38
|
+
<fieldset id="skin-choices"><legend>Skin</legend></fieldset>
|
|
39
|
+
<fieldset>
|
|
40
|
+
<legend>Mode</legend>
|
|
41
|
+
<label class="choice"><input type="radio" name="theme" value="light"><span class="mode-light">Light</span></label>
|
|
42
|
+
<label class="choice"><input type="radio" name="theme" value="dark"><span class="mode-dark">Dark</span></label>
|
|
43
|
+
</fieldset>
|
|
44
|
+
<fieldset>
|
|
45
|
+
<legend>Sound</legend>
|
|
46
|
+
<label class="choice" title="Plays when the session manager stops, remains unavailable after a brief recovery check, or a new Agent Guild update is discovered. Click or type on this page to allow playback."><input id="sound" type="checkbox" name="sound"><span class="sound-alerts">Alert sounds</span></label>
|
|
47
|
+
</fieldset>
|
|
48
|
+
<fieldset id="voice-choice" hidden>
|
|
49
|
+
<legend>Voice</legend>
|
|
50
|
+
<label class="choice"><input id="voice" type="checkbox" name="voice"><span class="voice-input">Voice input</span></label>
|
|
51
|
+
<p class="voice-note">Adds a Dictate button to terminals. Your browser may send the audio to its maker, such as Google or Microsoft, to turn it into text. Nothing is heard until you press Dictate, and Enter is never pressed for you.</p>
|
|
52
|
+
</fieldset>
|
|
53
|
+
</div>
|
|
54
|
+
<button id="manager" class="btn manager menu-button" type="button" popovertarget="manager-menu" aria-haspopup="menu" hidden>Manager</button>
|
|
55
|
+
<div id="manager-menu" class="manager-menu menu" popover role="menu" aria-label="Session manager">
|
|
56
|
+
<button id="restart-manager" class="menu-item restart-manager" type="button" role="menuitem" hidden>Restart manager</button>
|
|
57
|
+
<button id="stop-manager" class="menu-item danger stop-manager" type="button" role="menuitem" title="Stop the session manager. This ends every session.">Stop manager</button>
|
|
58
|
+
</div>
|
|
49
59
|
</div>
|
|
50
|
-
<button id="notes-open" class="btn notes-open" type="button" aria-haspopup="dialog">Notes</button>
|
|
51
|
-
<button id="restart-manager" class="btn restart-manager" type="button" hidden>Restart manager</button>
|
|
52
|
-
<button id="stop-manager" class="btn danger stop-manager" type="button" hidden title="Stop the session manager. This ends every session.">Stop manager</button>
|
|
53
60
|
</div>
|
|
54
61
|
</header>
|
|
55
62
|
|
|
@@ -58,10 +65,25 @@
|
|
|
58
65
|
<div class="section-head">
|
|
59
66
|
<h2 id="launch-heading">Start a session</h2>
|
|
60
67
|
<div class="launch-tools">
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
68
|
+
<div class="cwd">
|
|
69
|
+
<label for="cwd">Working folder</label>
|
|
70
|
+
<div class="cwd-field">
|
|
71
|
+
<input id="cwd" type="text" spellcheck="false" autocomplete="off" placeholder="Home folder">
|
|
72
|
+
<div class="cwd-tools">
|
|
73
|
+
<button id="cwd-pick" class="cwd-tool" type="button" aria-label="Choose a folder…" title="Connect to the session manager to choose folders" disabled>
|
|
74
|
+
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
|
75
|
+
<path d="M3.5 9V6.5A1.5 1.5 0 0 1 5 5h4l2 2h7.5A1.5 1.5 0 0 1 20 8.5V10"/>
|
|
76
|
+
<path d="M3.5 10.5H21l-2.5 8H5.5z"/>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
79
|
+
<button id="cwd-open" class="cwd-tool" type="button" aria-label="Open working folder in file manager" title="Connect to the session manager to open folders" disabled>
|
|
80
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
|
81
|
+
<path d="M7 17L17 7M9 7h8v8"/>
|
|
82
|
+
</svg>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
65
87
|
<button id="github-open" class="btn github-open" type="button" aria-haspopup="dialog">Clone from GitHub…</button>
|
|
66
88
|
</div>
|
|
67
89
|
</div>
|
|
@@ -118,23 +140,110 @@
|
|
|
118
140
|
<div class="panel-bar">
|
|
119
141
|
<div class="panel-title">
|
|
120
142
|
<span id="panel-icon" class="provider-icon small"></span>
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
</
|
|
143
|
+
<button id="panel-switch" class="panel-switch" type="button" popovertarget="panel-sessions" aria-haspopup="menu" title="Show another session here">
|
|
144
|
+
<span id="panel-title" class="title"></span>
|
|
145
|
+
<span id="panel-sub" class="sub"></span>
|
|
146
|
+
</button>
|
|
125
147
|
<div id="panel-agents" class="agents"></div>
|
|
126
148
|
</div>
|
|
127
149
|
<div class="panel-actions">
|
|
150
|
+
<button id="panel-split" class="btn split" type="button" popovertarget="panel-sessions" aria-haspopup="menu" title="Show a second session beside this one" hidden>Split</button>
|
|
151
|
+
<button id="panel-swap" class="btn swap" type="button" hidden></button>
|
|
128
152
|
<button id="panel-voice" class="btn voice" type="button" hidden aria-pressed="false" title="Speak into this terminal. Review the text, then press Enter yourself.">Dictate</button>
|
|
129
153
|
<button id="panel-copy" class="btn" type="button" hidden aria-haspopup="dialog" title="Select and copy terminal text">Copy…</button>
|
|
130
154
|
<button id="panel-stop" class="btn danger" type="button">Stop</button>
|
|
131
|
-
<button id="panel-close" class="btn" type="button" title="Hide the
|
|
155
|
+
<button id="panel-close" class="btn" type="button" title="Hide the terminals. The sessions keep running.">Hide</button>
|
|
132
156
|
</div>
|
|
133
157
|
</div>
|
|
134
|
-
<div id="
|
|
158
|
+
<div id="panel-sessions" class="session-menu menu" popover role="menu" aria-label="Sessions"></div>
|
|
159
|
+
<div id="terminal-panes" class="terminal-panes">
|
|
160
|
+
<section class="terminal-pane" data-pane="0">
|
|
161
|
+
<div class="pane-head"><span class="pane-name"></span><button class="pane-close" type="button" aria-label="Close this terminal" title="Close this terminal. The session keeps running."></button></div>
|
|
162
|
+
<div class="terminal-host"></div>
|
|
163
|
+
</section>
|
|
164
|
+
<div id="pane-splitter" class="splitter pane-splitter" role="separator" tabindex="0" aria-label="Resize the terminals" hidden></div>
|
|
165
|
+
<section class="terminal-pane" data-pane="1" hidden>
|
|
166
|
+
<div class="pane-head"><span class="pane-name"></span><button class="pane-close" type="button" aria-label="Close this terminal" title="Close this terminal. The session keeps running."></button></div>
|
|
167
|
+
<div class="terminal-host"></div>
|
|
168
|
+
</section>
|
|
169
|
+
</div>
|
|
135
170
|
<div id="voice-preview" class="voice-preview" hidden aria-live="polite"></div>
|
|
136
171
|
</div>
|
|
137
172
|
|
|
173
|
+
<aside id="dock" class="dock" aria-label="Side panel" hidden>
|
|
174
|
+
<div id="dock-splitter" class="splitter dock-splitter" role="separator" aria-orientation="vertical" tabindex="0" aria-label="Resize the side panel"></div>
|
|
175
|
+
<div class="dock-bar">
|
|
176
|
+
<div class="dock-tabs" role="tablist" aria-label="Side panel">
|
|
177
|
+
<button id="github-title" class="dock-tab github-tab" type="button" role="tab" aria-controls="github" aria-selected="false">GitHub</button>
|
|
178
|
+
<button id="notes-title" class="dock-tab notes-tab" type="button" role="tab" aria-controls="notes" aria-selected="false">Notes</button>
|
|
179
|
+
</div>
|
|
180
|
+
<button id="dock-close" class="btn" type="button">Close</button>
|
|
181
|
+
</div>
|
|
182
|
+
<section id="github" class="dock-panel github" role="tabpanel" aria-labelledby="github-title" hidden>
|
|
183
|
+
<p id="github-sub" class="dock-sub sub"></p>
|
|
184
|
+
<div id="github-accounts" class="github-accounts" hidden>
|
|
185
|
+
<div id="github-chips" class="github-chips" role="tablist" aria-label="Repository management accounts"></div>
|
|
186
|
+
<button id="github-add" class="btn github-add" type="button">Add account</button>
|
|
187
|
+
</div>
|
|
188
|
+
<div id="github-picker" class="github-picker" hidden>
|
|
189
|
+
<div class="github-combo">
|
|
190
|
+
<input id="github-repo" type="text" role="combobox" aria-label="Repository" aria-autocomplete="list" aria-expanded="false" aria-controls="github-repo-list" placeholder="Search repositories" spellcheck="false" autocomplete="off">
|
|
191
|
+
<ul id="github-repo-list" class="github-repo-list" role="listbox" aria-label="Repositories" hidden></ul>
|
|
192
|
+
</div>
|
|
193
|
+
<button id="github-repo-reload" class="btn" type="button" title="Load the repositories of every account again">Reload</button>
|
|
194
|
+
</div>
|
|
195
|
+
<p id="github-picker-note" class="github-picker-note" hidden></p>
|
|
196
|
+
<div id="github-views" class="github-views" role="tablist" aria-label="GitHub views" hidden>
|
|
197
|
+
<button id="github-view-repos" class="github-view" type="button" role="tab" aria-controls="github-repos" aria-selected="true" data-view="repos" aria-label="Repositories"><span class="long">Repositories</span><span class="short" aria-hidden="true">Repos</span></button>
|
|
198
|
+
<button id="github-view-issues" class="github-view" type="button" role="tab" aria-controls="github-issues" aria-selected="false" data-view="issues" tabindex="-1">Issues</button>
|
|
199
|
+
<button id="github-view-actions" class="github-view" type="button" role="tab" aria-controls="github-runs" aria-selected="false" data-view="actions" tabindex="-1" aria-label="Actions">Actions<span id="github-running" class="github-running" hidden></span></button>
|
|
200
|
+
<button id="github-view-pulls" class="github-view" type="button" role="tab" aria-controls="github-pulls" aria-selected="false" data-view="pulls" tabindex="-1" aria-label="Pull requests"><span class="long">Pull requests</span><span class="short" aria-hidden="true">PRs</span></button>
|
|
201
|
+
</div>
|
|
202
|
+
<div id="github-repos" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-repos">
|
|
203
|
+
<section id="github-card" class="github-card" aria-live="polite" hidden></section>
|
|
204
|
+
<div id="github-status" class="github-status" hidden></div>
|
|
205
|
+
<div id="github-tools" class="history-tools github-tools" hidden>
|
|
206
|
+
<input id="github-filter" type="search" placeholder="Filter repositories" aria-label="Filter repositories" spellcheck="false" autocomplete="off">
|
|
207
|
+
<button id="github-refresh" class="btn" type="button">Refresh</button>
|
|
208
|
+
<button id="github-new" class="btn" type="button" aria-expanded="false" aria-controls="github-create">New repository</button>
|
|
209
|
+
</div>
|
|
210
|
+
<form id="github-create" class="github-card github-create" hidden>
|
|
211
|
+
<h3>New repository</h3>
|
|
212
|
+
<div class="github-fields">
|
|
213
|
+
<label><span>Owner</span><select id="github-create-owner"></select></label>
|
|
214
|
+
<label><span>Name</span><input id="github-create-name" type="text" required maxlength="100" pattern="[A-Za-z0-9._\-]+" spellcheck="false" autocomplete="off" placeholder="my-project"></label>
|
|
215
|
+
<label class="wide"><span>Description</span><input id="github-create-description" type="text" maxlength="350" autocomplete="off" placeholder="Optional"></label>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="github-options">
|
|
218
|
+
<label><input id="github-create-private" type="checkbox" checked> Private</label>
|
|
219
|
+
<label><input id="github-create-readme" type="checkbox" checked> Start with a README</label>
|
|
220
|
+
<label><input id="github-create-clone" type="checkbox" checked> <span id="github-create-clone-label">Clone it</span></label>
|
|
221
|
+
</div>
|
|
222
|
+
<p id="github-create-error" class="github-error" role="alert" hidden></p>
|
|
223
|
+
<div class="github-actions">
|
|
224
|
+
<button id="github-create-submit" class="btn primary" type="submit">Create repository</button>
|
|
225
|
+
<button id="github-create-cancel" class="btn" type="button">Cancel</button>
|
|
226
|
+
</div>
|
|
227
|
+
</form>
|
|
228
|
+
<div id="github-list" class="history-list" role="list"></div>
|
|
229
|
+
<p id="github-note" class="models-note" hidden></p>
|
|
230
|
+
</div>
|
|
231
|
+
<div id="github-issues" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-issues" hidden></div>
|
|
232
|
+
<div id="github-runs" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-actions" hidden></div>
|
|
233
|
+
<div id="github-pulls" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-pulls" hidden></div>
|
|
234
|
+
<div id="github-clone-into" class="history-form github-form">
|
|
235
|
+
<label for="github-parent">Clone into</label>
|
|
236
|
+
<input id="github-parent" type="text" placeholder="Folder that holds your clones" spellcheck="false" autocomplete="off">
|
|
237
|
+
</div>
|
|
238
|
+
</section>
|
|
239
|
+
<section id="notes" class="dock-panel notes-panel" role="tabpanel" aria-labelledby="notes-title" hidden>
|
|
240
|
+
<p id="notes-sub" class="dock-sub sub" aria-live="polite">Saved in this browser as you type</p>
|
|
241
|
+
<div class="dock-body">
|
|
242
|
+
<textarea id="notes-text" class="notes-text" aria-labelledby="notes-title" aria-describedby="notes-sub" autocomplete="off" placeholder="Prompts, commands, to-dos: anything you want to keep at hand"></textarea>
|
|
243
|
+
</div>
|
|
244
|
+
</section>
|
|
245
|
+
</aside>
|
|
246
|
+
|
|
138
247
|
<dialog id="terminal-copy" class="terminal-copy" aria-labelledby="terminal-copy-title">
|
|
139
248
|
<div class="terminal-copy-bar">
|
|
140
249
|
<div class="terminal-copy-heading">
|
|
@@ -204,19 +313,6 @@
|
|
|
204
313
|
<p id="changelog-status" class="news-status" hidden></p>
|
|
205
314
|
</dialog>
|
|
206
315
|
|
|
207
|
-
<dialog id="notes" class="news-panel notes-panel" aria-labelledby="notes-title">
|
|
208
|
-
<div class="news-bar">
|
|
209
|
-
<div class="news-heading">
|
|
210
|
-
<h2 id="notes-title">Notes</h2>
|
|
211
|
-
<p id="notes-sub" class="sub" aria-live="polite">Saved in this browser as you type</p>
|
|
212
|
-
</div>
|
|
213
|
-
<button id="notes-close" class="btn" type="button">Close</button>
|
|
214
|
-
</div>
|
|
215
|
-
<div class="news-body">
|
|
216
|
-
<textarea id="notes-text" class="notes-text" aria-labelledby="notes-title" aria-describedby="notes-sub" autocomplete="off" placeholder="Prompts, commands, to-dos: anything you want to keep at hand"></textarea>
|
|
217
|
-
</div>
|
|
218
|
-
</dialog>
|
|
219
|
-
|
|
220
316
|
<dialog id="history" class="models history" aria-labelledby="history-title">
|
|
221
317
|
<div class="models-bar">
|
|
222
318
|
<span id="history-icon" class="provider-icon small"></span>
|
|
@@ -241,54 +337,6 @@
|
|
|
241
337
|
</form>
|
|
242
338
|
</dialog>
|
|
243
339
|
|
|
244
|
-
<dialog id="github" class="models history github" aria-labelledby="github-title">
|
|
245
|
-
<div class="models-bar">
|
|
246
|
-
<span class="github-mark" aria-hidden="true"></span>
|
|
247
|
-
<div class="models-heading">
|
|
248
|
-
<h2 id="github-title">GitHub</h2>
|
|
249
|
-
<p id="github-sub" class="sub"></p>
|
|
250
|
-
</div>
|
|
251
|
-
<button id="github-close" class="btn" type="button">Close</button>
|
|
252
|
-
</div>
|
|
253
|
-
<div id="github-accounts" class="github-accounts" hidden>
|
|
254
|
-
<div id="github-chips" class="github-chips" role="tablist" aria-label="GitHub accounts"></div>
|
|
255
|
-
<button id="github-add" class="btn github-add" type="button">Add account</button>
|
|
256
|
-
</div>
|
|
257
|
-
<div class="models-body">
|
|
258
|
-
<section id="github-card" class="github-card" aria-live="polite" hidden></section>
|
|
259
|
-
<div id="github-status" class="github-status" hidden></div>
|
|
260
|
-
<div id="github-tools" class="history-tools github-tools" hidden>
|
|
261
|
-
<input id="github-filter" type="search" placeholder="Filter repositories" aria-label="Filter repositories" spellcheck="false" autocomplete="off">
|
|
262
|
-
<button id="github-refresh" class="btn" type="button">Refresh</button>
|
|
263
|
-
<button id="github-new" class="btn" type="button" aria-expanded="false" aria-controls="github-create">New repository</button>
|
|
264
|
-
</div>
|
|
265
|
-
<form id="github-create" class="github-card github-create" hidden>
|
|
266
|
-
<h3>New repository</h3>
|
|
267
|
-
<div class="github-fields">
|
|
268
|
-
<label><span>Owner</span><select id="github-create-owner"></select></label>
|
|
269
|
-
<label><span>Name</span><input id="github-create-name" type="text" required maxlength="100" pattern="[A-Za-z0-9._\-]+" spellcheck="false" autocomplete="off" placeholder="my-project"></label>
|
|
270
|
-
<label class="wide"><span>Description</span><input id="github-create-description" type="text" maxlength="350" autocomplete="off" placeholder="Optional"></label>
|
|
271
|
-
</div>
|
|
272
|
-
<div class="github-options">
|
|
273
|
-
<label><input id="github-create-private" type="checkbox" checked> Private</label>
|
|
274
|
-
<label><input id="github-create-readme" type="checkbox" checked> Start with a README</label>
|
|
275
|
-
<label><input id="github-create-clone" type="checkbox" checked> <span id="github-create-clone-label">Clone it</span></label>
|
|
276
|
-
</div>
|
|
277
|
-
<p id="github-create-error" class="github-error" role="alert" hidden></p>
|
|
278
|
-
<div class="github-actions">
|
|
279
|
-
<button id="github-create-submit" class="btn primary" type="submit">Create repository</button>
|
|
280
|
-
<button id="github-create-cancel" class="btn" type="button">Cancel</button>
|
|
281
|
-
</div>
|
|
282
|
-
</form>
|
|
283
|
-
<div id="github-list" class="history-list" role="list"></div>
|
|
284
|
-
<p id="github-note" class="models-note" hidden></p>
|
|
285
|
-
</div>
|
|
286
|
-
<div class="history-form github-form">
|
|
287
|
-
<label for="github-parent">Clone into</label>
|
|
288
|
-
<input id="github-parent" type="text" placeholder="Folder that holds your clones" spellcheck="false" autocomplete="off">
|
|
289
|
-
</div>
|
|
290
|
-
</dialog>
|
|
291
|
-
|
|
292
340
|
<div id="tip" class="tip" aria-hidden="true" hidden><strong id="tip-title"></strong><span id="tip-source"></span><p id="tip-text"></p></div>
|
|
293
341
|
|
|
294
342
|
<div id="toast" class="toast" role="alert" hidden></div>
|
package/web/layout.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const TOPBAR_INLINE_MIN = 900;
|
|
2
|
+
export const DOCK_PUSH_MIN = 1280;
|
|
3
|
+
export const DOCK_FULL_MAX = 639;
|
|
4
|
+
export const DOCK_DEFAULT = 420;
|
|
5
|
+
export const DOCK_MIN = 320;
|
|
6
|
+
export const DOCK_MAX = 720;
|
|
7
|
+
export const WORKSPACE_MIN = 560;
|
|
8
|
+
export const SPLIT_COLUMNS_MIN = 1000;
|
|
9
|
+
export const SPLIT_ROWS_MIN_HEIGHT = 620;
|
|
10
|
+
export const SPLIT_ROWS_MIN_WIDTH = 560;
|
|
11
|
+
export const SPLIT_RATIO_MIN = 0.25;
|
|
12
|
+
|
|
13
|
+
export function topbarInline(width) {
|
|
14
|
+
return width >= TOPBAR_INLINE_MIN;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function dockMode(width) {
|
|
18
|
+
if (width <= DOCK_FULL_MAX) return 'full';
|
|
19
|
+
return width >= DOCK_PUSH_MIN ? 'push' : 'over';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function clampDockWidth(value, windowWidth) {
|
|
23
|
+
const max = Math.max(DOCK_MIN, Math.min(DOCK_MAX, windowWidth - WORKSPACE_MIN));
|
|
24
|
+
const wanted = typeof value === 'number' && !Number.isNaN(value) ? value : DOCK_DEFAULT;
|
|
25
|
+
return Math.round(Math.min(max, Math.max(DOCK_MIN, wanted)));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function stageBesideDock(windowWidth, dockWidth) {
|
|
29
|
+
return dockMode(windowWidth) !== 'full' && windowWidth - dockWidth - 32 >= WORKSPACE_MIN;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function splitMode(width, height) {
|
|
33
|
+
if (width >= SPLIT_COLUMNS_MIN) return 'columns';
|
|
34
|
+
if (width >= SPLIT_ROWS_MIN_WIDTH && height >= SPLIT_ROWS_MIN_HEIGHT) return 'rows';
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function clampRatio(value) {
|
|
39
|
+
const ratio = Number.isFinite(value) ? value : 0.5;
|
|
40
|
+
return Math.min(1 - SPLIT_RATIO_MIN, Math.max(SPLIT_RATIO_MIN, ratio));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function bindSplitter(handle, { axis = () => 'x', start, move, end, step = 24, home = null, endKey = null }) {
|
|
44
|
+
let drag = null;
|
|
45
|
+
const coord = (e) => (axis() === 'x' ? e.clientX : e.clientY);
|
|
46
|
+
handle.addEventListener('pointerdown', (e) => {
|
|
47
|
+
if (e.button !== 0) return;
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
handle.setPointerCapture(e.pointerId);
|
|
50
|
+
drag = { id: e.pointerId, at: coord(e), from: start() };
|
|
51
|
+
handle.classList.add('dragging');
|
|
52
|
+
});
|
|
53
|
+
handle.addEventListener('pointermove', (e) => {
|
|
54
|
+
if (drag?.id === e.pointerId) move(coord(e) - drag.at, drag.from);
|
|
55
|
+
});
|
|
56
|
+
const finish = (e) => {
|
|
57
|
+
if (drag?.id !== e.pointerId) return;
|
|
58
|
+
drag = null;
|
|
59
|
+
handle.classList.remove('dragging');
|
|
60
|
+
end();
|
|
61
|
+
};
|
|
62
|
+
handle.addEventListener('pointerup', finish);
|
|
63
|
+
handle.addEventListener('pointercancel', finish);
|
|
64
|
+
handle.addEventListener('lostpointercapture', finish);
|
|
65
|
+
handle.addEventListener('keydown', (e) => {
|
|
66
|
+
const back = axis() === 'x' ? 'ArrowLeft' : 'ArrowUp';
|
|
67
|
+
const forward = axis() === 'x' ? 'ArrowRight' : 'ArrowDown';
|
|
68
|
+
if (e.key === back || e.key === forward) move(e.key === back ? -step : step, start());
|
|
69
|
+
else if (e.key === 'Home' && home) home();
|
|
70
|
+
else if (e.key === 'End' && endKey) endKey();
|
|
71
|
+
else return;
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
end();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Ranking and highlighting for the GitHub panel's repository picker, kept free of the page so tests can run it.
|
|
2
|
+
|
|
3
|
+
const RECENT_MAX = 8;
|
|
4
|
+
|
|
5
|
+
function haystack(repo) {
|
|
6
|
+
return [repo.fullName, repo.name, repo.description, repo.language, repo.login].filter(Boolean).join(' ').toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Every whitespace-separated word must appear in the owner/name, description,
|
|
11
|
+
* language or account login. A name that starts with the whole query ranks
|
|
12
|
+
* first, then owner/name containing it, then the rest; ties go to the latest push.
|
|
13
|
+
* An empty query keeps every repository, latest push first.
|
|
14
|
+
*/
|
|
15
|
+
export function rankRepos(repos, query) {
|
|
16
|
+
const q = String(query ?? '').trim().toLowerCase();
|
|
17
|
+
const words = q.split(/\s+/).filter(Boolean);
|
|
18
|
+
const ranked = [];
|
|
19
|
+
for (const repo of repos) {
|
|
20
|
+
if (!words.length) {
|
|
21
|
+
ranked.push({ repo, rank: 3 });
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!words.every((word) => haystack(repo).includes(word))) continue;
|
|
25
|
+
const rank = String(repo.name ?? '').toLowerCase().startsWith(q) ? 0 : String(repo.fullName ?? '').toLowerCase().includes(q) ? 1 : 2;
|
|
26
|
+
ranked.push({ repo, rank });
|
|
27
|
+
}
|
|
28
|
+
ranked.sort((a, b) => a.rank - b.rank
|
|
29
|
+
|| (Date.parse(b.repo.pushedAt) || 0) - (Date.parse(a.repo.pushedAt) || 0)
|
|
30
|
+
|| String(a.repo.fullName).localeCompare(String(b.repo.fullName))
|
|
31
|
+
|| String(a.repo.accountId).localeCompare(String(b.repo.accountId)));
|
|
32
|
+
return ranked.map((item) => item.repo);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** One repository as seen by one account: the same repository through two accounts is two picks. */
|
|
36
|
+
export function repoKey(repo) {
|
|
37
|
+
return `${repo.accountId}:${repo.fullName}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Up to eight recent picks first, then the rest in their order, each once. */
|
|
41
|
+
export function recentFirst(repos, keys) {
|
|
42
|
+
const byKey = new Map(repos.map((repo) => [repoKey(repo), repo]));
|
|
43
|
+
const pinned = [];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
for (const key of Array.isArray(keys) ? keys : []) {
|
|
46
|
+
const repo = byKey.get(key);
|
|
47
|
+
if (!repo || seen.has(key)) continue;
|
|
48
|
+
pinned.push(repo);
|
|
49
|
+
seen.add(key);
|
|
50
|
+
if (pinned.length === RECENT_MAX) break;
|
|
51
|
+
}
|
|
52
|
+
return [...pinned, ...repos.filter((repo) => !seen.has(repoKey(repo)))];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The keys with `repo` moved to the front, at most eight. */
|
|
56
|
+
export function remember(keys, repo) {
|
|
57
|
+
const key = repoKey(repo);
|
|
58
|
+
return [key, ...(Array.isArray(keys) ? keys : []).filter((k) => k !== key)].slice(0, RECENT_MAX);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The pick for a folder's origin (lowercase owner/name): the account picked most recently, else the first that has it. */
|
|
62
|
+
export function repoForOrigin(repos, origin, keys = []) {
|
|
63
|
+
if (!origin) return null;
|
|
64
|
+
const matches = repos.filter((repo) => String(repo.fullName).toLowerCase() === origin);
|
|
65
|
+
if (!matches.length) return null;
|
|
66
|
+
const order = Array.isArray(keys) ? keys : [];
|
|
67
|
+
return recentFirst(matches, order)[0];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Splits text into matched and unmatched runs for the query's words, matched literally. */
|
|
71
|
+
export function highlightParts(text, query) {
|
|
72
|
+
const source = String(text ?? '');
|
|
73
|
+
const words = String(query ?? '').trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
74
|
+
if (!source || !words.length) return [{ match: false, text: source }];
|
|
75
|
+
const lower = source.toLowerCase();
|
|
76
|
+
const marks = new Array(source.length).fill(false);
|
|
77
|
+
for (const word of words) {
|
|
78
|
+
for (let at = lower.indexOf(word); at !== -1; at = lower.indexOf(word, at + word.length)) {
|
|
79
|
+
marks.fill(true, at, at + word.length);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const parts = [];
|
|
83
|
+
let start = 0;
|
|
84
|
+
for (let i = 1; i <= source.length; i++) {
|
|
85
|
+
if (i === source.length || marks[i] !== marks[start]) {
|
|
86
|
+
parts.push({ match: marks[start], text: source.slice(start, i) });
|
|
87
|
+
start = i;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return parts;
|
|
91
|
+
}
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
246
246
|
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--panel-strong); }
|
|
247
247
|
|
|
248
|
-
.models, .news-panel, .auth, .news-latest, .
|
|
248
|
+
.models, .news-panel, .dock, .auth, .news-latest, .menu:popover-open { background: var(--panel-strong); backdrop-filter: blur(16px); }
|
|
249
249
|
.models, .news-panel { box-shadow: var(--dialog-shadow); }
|
|
250
250
|
.model-columns, .news-day { background: transparent; backdrop-filter: blur(16px); }
|
|
251
251
|
.models::backdrop, .news-panel::backdrop { background: rgba(40, 24, 8, 0.4); backdrop-filter: blur(2px); }
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
background: var(--page-veil), var(--page-art) center bottom / cover no-repeat;
|
|
51
51
|
}
|
|
52
52
|
/* Text that sits on the town itself gets a halo of the page colour. */
|
|
53
|
-
.section-head h2, .cwd >
|
|
53
|
+
.section-head h2, .cwd > label, .empty { text-shadow: 0 0 2px var(--bg), 0 0 8px var(--bg), 0 0 16px var(--bg); }
|
|
54
54
|
h2 { font-weight: 400; letter-spacing: 0.03em; font-size: 1.22rem; }
|
|
55
55
|
|
|
56
56
|
.topbar { background: var(--panel); backdrop-filter: blur(12px) saturate(1.1); border-bottom: 1px solid transparent; border-image: var(--rule) 1; }
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
252
252
|
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--panel-strong); }
|
|
253
253
|
|
|
254
|
-
.models, .news-panel, .auth, .news-latest, .
|
|
254
|
+
.models, .news-panel, .dock, .auth, .news-latest, .menu:popover-open { background: var(--panel-strong); backdrop-filter: blur(16px); }
|
|
255
255
|
.models, .news-panel { box-shadow: var(--dialog-shadow); }
|
|
256
256
|
.model-columns, .news-day { background: transparent; backdrop-filter: blur(16px); }
|
|
257
257
|
.models::backdrop, .news-panel::backdrop { background: rgba(18, 26, 20, 0.42); backdrop-filter: blur(2px); }
|
package/web/skins/grove/skin.css
CHANGED
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
247
247
|
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--paper-strong); }
|
|
248
248
|
|
|
249
|
-
.models, .news-panel, .auth, .news-latest, .
|
|
249
|
+
.models, .news-panel, .dock, .auth, .news-latest, .menu:popover-open { background: var(--paper-strong); backdrop-filter: blur(16px); }
|
|
250
250
|
.models, .news-panel { box-shadow: var(--dialog-shadow); }
|
|
251
251
|
.model-columns, .news-day { background: transparent; backdrop-filter: blur(16px); }
|
|
252
252
|
.models::backdrop, .news-panel::backdrop { background: rgba(20, 36, 26, 0.34); backdrop-filter: blur(3px); }
|
package/web/skins/guild/skin.css
CHANGED
|
@@ -343,10 +343,10 @@
|
|
|
343
343
|
border-radius: 6px; border-color: #4a3d25; background: var(--card-bg);
|
|
344
344
|
box-shadow: inset 0 0 0 1px rgba(232, 196, 124, 0.06), 0 8px 24px rgba(0, 0, 0, 0.35);
|
|
345
345
|
}
|
|
346
|
-
.news-row + .news-row, .news-bar, .news-filters, .news-item, .news-status, .changelog-release + .changelog-release { border-color: #2a2418; }
|
|
346
|
+
.news-row + .news-row, .news-bar, .dock-bar, .news-filters, .news-item, .news-status, .changelog-release + .changelog-release { border-color: #2a2418; }
|
|
347
347
|
.news-link { color: #f3ead8; }
|
|
348
348
|
a.news-link:visited { color: var(--muted); }
|
|
349
|
-
.news-panel {
|
|
349
|
+
.news-panel, .dock {
|
|
350
350
|
background: #12141a; border-left-color: #4a3d25;
|
|
351
351
|
box-shadow: inset 1px 0 0 rgba(232, 196, 124, 0.08), -24px 0 60px rgba(0, 0, 0, 0.6);
|
|
352
352
|
}
|
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
.model-toggle:hover { background: #f6eedd; }
|
|
459
459
|
|
|
460
460
|
.news-latest { border-color: #e4d6b6; box-shadow: 0 1px 2px rgba(70, 48, 12, 0.08), 0 10px 26px rgba(70, 48, 12, 0.08); }
|
|
461
|
-
.news-panel { border-left-color: #e2d3b2; box-shadow: -24px 0 60px rgba(40, 28, 8, 0.22); }
|
|
461
|
+
.news-panel, .dock { border-left-color: #e2d3b2; box-shadow: -24px 0 60px rgba(40, 28, 8, 0.22); }
|
|
462
462
|
.news-panel::backdrop { background: rgba(40, 30, 14, 0.3); }
|
|
463
463
|
|
|
464
464
|
.status-pill { background: #f6efe0; border: 1px solid #e2d3b2; }
|
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
249
249
|
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--glass-strong); }
|
|
250
250
|
|
|
251
|
-
.models, .news-panel, .auth, .news-latest, .
|
|
251
|
+
.models, .news-panel, .dock, .auth, .news-latest, .menu:popover-open { background: var(--glass-strong); backdrop-filter: blur(16px); }
|
|
252
252
|
.models, .news-panel { box-shadow: var(--dialog-shadow); }
|
|
253
253
|
.model-columns, .news-day { background: transparent; backdrop-filter: blur(16px); }
|
|
254
254
|
.models::backdrop, .news-panel::backdrop { background: rgba(10, 16, 40, 0.38); backdrop-filter: blur(2px); }
|