@oddessentials/agent-guild 0.25.0 → 0.27.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.
Files changed (56) hide show
  1. package/README.md +44 -10
  2. package/bin/agent-guild.mjs +2 -0
  3. package/node_modules/qrcode-generator/README.md +101 -0
  4. package/node_modules/qrcode-generator/dist/qrcode.d.ts +57 -0
  5. package/node_modules/qrcode-generator/dist/qrcode.js +2297 -0
  6. package/node_modules/qrcode-generator/dist/qrcode.mjs +2237 -0
  7. package/node_modules/qrcode-generator/dist/qrcode_SJIS.js +30 -0
  8. package/node_modules/qrcode-generator/dist/qrcode_SJIS.mjs +23 -0
  9. package/node_modules/qrcode-generator/dist/qrcode_UTF8.js +26 -0
  10. package/node_modules/qrcode-generator/dist/qrcode_UTF8.mjs +50 -0
  11. package/node_modules/qrcode-generator/experiment/vt/package-lock.json +6184 -0
  12. package/node_modules/qrcode-generator/experiment/vt/package.json +19 -0
  13. package/node_modules/qrcode-generator/experiment/vt/src/main/qrcode.ts +2311 -0
  14. package/node_modules/qrcode-generator/experiment/vt/src/main/qrcode_SJIS.ts +25 -0
  15. package/node_modules/qrcode-generator/experiment/vt/src/main/qrcode_UTF8.ts +50 -0
  16. package/node_modules/qrcode-generator/experiment/vt/src/main/types.ts +80 -0
  17. package/node_modules/qrcode-generator/experiment/vt/src/test/qrcode-test-impl.js +353 -0
  18. package/node_modules/qrcode-generator/experiment/vt/src/test/qrcode.test.mjs +5 -0
  19. package/node_modules/qrcode-generator/experiment/vt/src/test/qrcode_SJIS.test.mjs +10 -0
  20. package/node_modules/qrcode-generator/experiment/vt/src/test/qrcode_UTF8.test.mjs +9 -0
  21. package/node_modules/qrcode-generator/experiment/vt/tsconfig.json +25 -0
  22. package/node_modules/qrcode-generator/experiment/vt/vite.config.js +31 -0
  23. package/node_modules/qrcode-generator/experiment/vt/web-test-runner.config.mjs +7 -0
  24. package/node_modules/qrcode-generator/package.json +37 -0
  25. package/node_modules/qrcode-generator/test/qrcode-test-impl.js +353 -0
  26. package/node_modules/qrcode-generator/test/qrcode.test.html +18 -0
  27. package/node_modules/qrcode-generator/test/qrcode.test.mjs +5 -0
  28. package/node_modules/qrcode-generator/test/qrcode_SJIS.test.html +21 -0
  29. package/node_modules/qrcode-generator/test/qrcode_SJIS.test.mjs +10 -0
  30. package/node_modules/qrcode-generator/test/qrcode_UTF8.test.html +19 -0
  31. package/node_modules/qrcode-generator/test/qrcode_UTF8.test.mjs +9 -0
  32. package/node_modules/qrcode-generator/web-test-runner.config.mjs +8 -0
  33. package/package.json +3 -2
  34. package/src/manager/access-policy.mjs +39 -0
  35. package/src/manager/config.mjs +1 -0
  36. package/src/manager/folder-opener.mjs +96 -0
  37. package/src/manager/folder-picker.mjs +116 -0
  38. package/src/manager/folder-picker.ps1 +84 -0
  39. package/src/manager/github-views.mjs +171 -0
  40. package/src/manager/github.mjs +56 -1
  41. package/src/manager/main.mjs +11 -6
  42. package/src/manager/remote-access.mjs +286 -0
  43. package/src/manager/server.mjs +105 -18
  44. package/src/manager/tailscale.mjs +201 -0
  45. package/web/activity-favicon.js +71 -0
  46. package/web/app.js +1154 -98
  47. package/web/index.html +205 -92
  48. package/web/layout.js +75 -0
  49. package/web/remote-access.js +236 -0
  50. package/web/repo-search.js +91 -0
  51. package/web/skins/gnomeland/skin.css +1 -1
  52. package/web/skins/goblinville/skin.css +2 -2
  53. package/web/skins/grove/skin.css +1 -1
  54. package/web/skins/guild/skin.css +3 -3
  55. package/web/skins/orbital/skin.css +1 -1
  56. package/web/styles.css +269 -38
package/web/index.html CHANGED
@@ -29,27 +29,35 @@
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="appearance" class="btn appearance" type="button" popovertarget="appearance-menu" aria-haspopup="dialog">Appearance</button>
33
- <div id="appearance-menu" class="appearance-menu" popover role="dialog" aria-label="Appearance">
34
- <fieldset id="skin-choices"><legend>Skin</legend></fieldset>
35
- <fieldset>
36
- <legend>Mode</legend>
37
- <label class="choice"><input type="radio" name="theme" value="light"><span class="mode-light">Light</span></label>
38
- <label class="choice"><input type="radio" name="theme" value="dark"><span class="mode-dark">Dark</span></label>
39
- </fieldset>
40
- <fieldset>
41
- <legend>Sound</legend>
42
- <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>
43
- </fieldset>
44
- <fieldset id="voice-choice" hidden>
45
- <legend>Voice</legend>
46
- <label class="choice"><input id="voice" type="checkbox" name="voice"><span class="voice-input">Voice input</span></label>
47
- <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>
48
- </fieldset>
49
- </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>
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
+ <button id="remote-access-open" class="btn" type="button" aria-haspopup="dialog" hidden>Remote access</button>
39
+ <fieldset id="skin-choices"><legend>Skin</legend></fieldset>
40
+ <fieldset>
41
+ <legend>Mode</legend>
42
+ <label class="choice"><input type="radio" name="theme" value="light"><span class="mode-light">Light</span></label>
43
+ <label class="choice"><input type="radio" name="theme" value="dark"><span class="mode-dark">Dark</span></label>
44
+ </fieldset>
45
+ <fieldset>
46
+ <legend>Sound</legend>
47
+ <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>
48
+ </fieldset>
49
+ <fieldset id="voice-choice" hidden>
50
+ <legend>Voice</legend>
51
+ <label class="choice"><input id="voice" type="checkbox" name="voice"><span class="voice-input">Voice input</span></label>
52
+ <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>
53
+ </fieldset>
54
+ </div>
55
+ <button id="manager" class="btn manager menu-button" type="button" popovertarget="manager-menu" aria-haspopup="menu" hidden>Manager</button>
56
+ <div id="manager-menu" class="manager-menu menu" popover role="menu" aria-label="Session manager">
57
+ <button id="restart-manager" class="menu-item restart-manager" type="button" role="menuitem" hidden>Restart manager</button>
58
+ <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>
59
+ </div>
60
+ </div>
53
61
  </div>
54
62
  </header>
55
63
 
@@ -58,10 +66,25 @@
58
66
  <div class="section-head">
59
67
  <h2 id="launch-heading">Start a session</h2>
60
68
  <div class="launch-tools">
61
- <label class="cwd">
62
- <span>Working folder</span>
63
- <input id="cwd" type="text" spellcheck="false" autocomplete="off" placeholder="Home folder">
64
- </label>
69
+ <div class="cwd">
70
+ <label for="cwd">Working folder</label>
71
+ <div class="cwd-field">
72
+ <input id="cwd" type="text" spellcheck="false" autocomplete="off" placeholder="Home folder">
73
+ <div class="cwd-tools">
74
+ <button id="cwd-pick" class="cwd-tool" type="button" aria-label="Choose a folder…" title="Connect to the session manager to choose folders" disabled>
75
+ <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">
76
+ <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"/>
77
+ <path d="M3.5 10.5H21l-2.5 8H5.5z"/>
78
+ </svg>
79
+ </button>
80
+ <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>
81
+ <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">
82
+ <path d="M7 17L17 7M9 7h8v8"/>
83
+ </svg>
84
+ </button>
85
+ </div>
86
+ </div>
87
+ </div>
65
88
  <button id="github-open" class="btn github-open" type="button" aria-haspopup="dialog">Clone from GitHub…</button>
66
89
  </div>
67
90
  </div>
@@ -118,23 +141,110 @@
118
141
  <div class="panel-bar">
119
142
  <div class="panel-title">
120
143
  <span id="panel-icon" class="provider-icon small"></span>
121
- <div>
122
- <div id="panel-title" class="title"></div>
123
- <div id="panel-sub" class="sub"></div>
124
- </div>
144
+ <button id="panel-switch" class="panel-switch" type="button" popovertarget="panel-sessions" aria-haspopup="menu" title="Show another session here">
145
+ <span id="panel-title" class="title"></span>
146
+ <span id="panel-sub" class="sub"></span>
147
+ </button>
125
148
  <div id="panel-agents" class="agents"></div>
126
149
  </div>
127
150
  <div class="panel-actions">
151
+ <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>
152
+ <button id="panel-swap" class="btn swap" type="button" hidden></button>
128
153
  <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
154
  <button id="panel-copy" class="btn" type="button" hidden aria-haspopup="dialog" title="Select and copy terminal text">Copy…</button>
130
155
  <button id="panel-stop" class="btn danger" type="button">Stop</button>
131
- <button id="panel-close" class="btn" type="button" title="Hide the terminal. The session keeps running.">Hide</button>
156
+ <button id="panel-close" class="btn" type="button" title="Hide the terminals. The sessions keep running.">Hide</button>
132
157
  </div>
133
158
  </div>
134
- <div id="terminal-host" class="terminal-host"></div>
159
+ <div id="panel-sessions" class="session-menu menu" popover role="menu" aria-label="Sessions"></div>
160
+ <div id="terminal-panes" class="terminal-panes">
161
+ <section class="terminal-pane" data-pane="0">
162
+ <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>
163
+ <div class="terminal-host"></div>
164
+ </section>
165
+ <div id="pane-splitter" class="splitter pane-splitter" role="separator" tabindex="0" aria-label="Resize the terminals" hidden></div>
166
+ <section class="terminal-pane" data-pane="1" hidden>
167
+ <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>
168
+ <div class="terminal-host"></div>
169
+ </section>
170
+ </div>
135
171
  <div id="voice-preview" class="voice-preview" hidden aria-live="polite"></div>
136
172
  </div>
137
173
 
174
+ <aside id="dock" class="dock" aria-label="Side panel" hidden>
175
+ <div id="dock-splitter" class="splitter dock-splitter" role="separator" aria-orientation="vertical" tabindex="0" aria-label="Resize the side panel"></div>
176
+ <div class="dock-bar">
177
+ <div class="dock-tabs" role="tablist" aria-label="Side panel">
178
+ <button id="github-title" class="dock-tab github-tab" type="button" role="tab" aria-controls="github" aria-selected="false">GitHub</button>
179
+ <button id="notes-title" class="dock-tab notes-tab" type="button" role="tab" aria-controls="notes" aria-selected="false">Notes</button>
180
+ </div>
181
+ <button id="dock-close" class="btn" type="button">Close</button>
182
+ </div>
183
+ <section id="github" class="dock-panel github" role="tabpanel" aria-labelledby="github-title" hidden>
184
+ <p id="github-sub" class="dock-sub sub"></p>
185
+ <div id="github-accounts" class="github-accounts" hidden>
186
+ <div id="github-chips" class="github-chips" role="tablist" aria-label="Repository management accounts"></div>
187
+ <button id="github-add" class="btn github-add" type="button">Add account</button>
188
+ </div>
189
+ <div id="github-picker" class="github-picker" hidden>
190
+ <div class="github-combo">
191
+ <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">
192
+ <ul id="github-repo-list" class="github-repo-list" role="listbox" aria-label="Repositories" hidden></ul>
193
+ </div>
194
+ <button id="github-repo-reload" class="btn" type="button" title="Load the repositories of every account again">Reload</button>
195
+ </div>
196
+ <p id="github-picker-note" class="github-picker-note" hidden></p>
197
+ <div id="github-views" class="github-views" role="tablist" aria-label="GitHub views" hidden>
198
+ <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>
199
+ <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>
200
+ <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>
201
+ <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>
202
+ </div>
203
+ <div id="github-repos" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-repos">
204
+ <section id="github-card" class="github-card" aria-live="polite" hidden></section>
205
+ <div id="github-status" class="github-status" hidden></div>
206
+ <div id="github-tools" class="history-tools github-tools" hidden>
207
+ <input id="github-filter" type="search" placeholder="Filter repositories" aria-label="Filter repositories" spellcheck="false" autocomplete="off">
208
+ <button id="github-refresh" class="btn" type="button">Refresh</button>
209
+ <button id="github-new" class="btn" type="button" aria-expanded="false" aria-controls="github-create">New repository</button>
210
+ </div>
211
+ <form id="github-create" class="github-card github-create" hidden>
212
+ <h3>New repository</h3>
213
+ <div class="github-fields">
214
+ <label><span>Owner</span><select id="github-create-owner"></select></label>
215
+ <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>
216
+ <label class="wide"><span>Description</span><input id="github-create-description" type="text" maxlength="350" autocomplete="off" placeholder="Optional"></label>
217
+ </div>
218
+ <div class="github-options">
219
+ <label><input id="github-create-private" type="checkbox" checked> Private</label>
220
+ <label><input id="github-create-readme" type="checkbox" checked> Start with a README</label>
221
+ <label><input id="github-create-clone" type="checkbox" checked> <span id="github-create-clone-label">Clone it</span></label>
222
+ </div>
223
+ <p id="github-create-error" class="github-error" role="alert" hidden></p>
224
+ <div class="github-actions">
225
+ <button id="github-create-submit" class="btn primary" type="submit">Create repository</button>
226
+ <button id="github-create-cancel" class="btn" type="button">Cancel</button>
227
+ </div>
228
+ </form>
229
+ <div id="github-list" class="history-list" role="list"></div>
230
+ <p id="github-note" class="models-note" hidden></p>
231
+ </div>
232
+ <div id="github-issues" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-issues" hidden></div>
233
+ <div id="github-runs" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-actions" hidden></div>
234
+ <div id="github-pulls" class="models-body github-body" role="tabpanel" aria-labelledby="github-view-pulls" hidden></div>
235
+ <div id="github-clone-into" class="history-form github-form">
236
+ <label for="github-parent">Clone into</label>
237
+ <input id="github-parent" type="text" placeholder="Folder that holds your clones" spellcheck="false" autocomplete="off">
238
+ </div>
239
+ </section>
240
+ <section id="notes" class="dock-panel notes-panel" role="tabpanel" aria-labelledby="notes-title" hidden>
241
+ <p id="notes-sub" class="dock-sub sub" aria-live="polite">Saved in this browser as you type</p>
242
+ <div class="dock-body">
243
+ <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>
244
+ </div>
245
+ </section>
246
+ </aside>
247
+
138
248
  <dialog id="terminal-copy" class="terminal-copy" aria-labelledby="terminal-copy-title">
139
249
  <div class="terminal-copy-bar">
140
250
  <div class="terminal-copy-heading">
@@ -150,6 +260,70 @@
150
260
  </div>
151
261
  </dialog>
152
262
 
263
+ <dialog id="remote-access" class="models remote-access" aria-labelledby="remote-access-title">
264
+ <div class="models-bar">
265
+ <div class="models-heading">
266
+ <h2 id="remote-access-title">Remote access</h2>
267
+ <p class="sub">Use Agent Guild on another device</p>
268
+ </div>
269
+ <button id="remote-close" class="btn" type="button">Close</button>
270
+ </div>
271
+ <div class="models-body remote-body">
272
+ <p>Access your running terminals through your Tailscale network. Your computer must be awake with Agent Guild and Tailscale running.</p>
273
+ <div class="remote-status" role="status" aria-live="polite" aria-atomic="true">
274
+ <strong id="remote-status-title">Checking connection…</strong>
275
+ <p id="remote-status-text"></p>
276
+ </div>
277
+ <p id="remote-error" class="remote-error" role="alert" hidden></p>
278
+ <div id="remote-address-row" hidden>
279
+ <label for="remote-address">Your remote address</label>
280
+ <input id="remote-address" class="remote-address" readonly spellcheck="false">
281
+ <div class="remote-actions">
282
+ <button id="remote-connect" class="btn primary" type="button" aria-expanded="false" aria-controls="remote-share">Connect another device</button>
283
+ <button id="remote-copy-address" class="btn" type="button">Copy address</button>
284
+ <a id="remote-open-address" class="btn" target="_blank" rel="noopener noreferrer">Open</a>
285
+ </div>
286
+ </div>
287
+ <div id="remote-share" class="remote-share" hidden>
288
+ <p>Connect the other device to Tailscale, then scan this code or open the sign-in link.</p>
289
+ <canvas id="remote-qr" role="img" aria-label="QR code to sign into Agent Guild" hidden></canvas>
290
+ <p id="remote-qr-note" class="sub"></p>
291
+ <label for="remote-signin">Anyone with this code or link can use your terminals.</label>
292
+ <input id="remote-signin" class="remote-address" readonly spellcheck="false" autocomplete="off">
293
+ <button id="remote-copy-signin" class="btn" type="button">Copy sign-in link</button>
294
+ </div>
295
+ <div class="remote-actions">
296
+ <button id="remote-primary" class="btn primary" type="button">Enable remote access</button>
297
+ <a id="remote-help" class="btn primary" target="_blank" rel="noopener noreferrer" hidden></a>
298
+ <button id="remote-check" class="btn" type="button">Check connection</button>
299
+ <button id="remote-disable" class="btn danger" type="button" hidden>Disable remote access</button>
300
+ </div>
301
+ <div id="remote-disable-confirm" class="remote-confirm" hidden>
302
+ <p>Disconnect remote browsers? Your terminals will keep running. Local access stays available.</p>
303
+ <div class="remote-actions">
304
+ <button id="remote-disable-yes" class="btn danger" type="button">Disable remote access</button>
305
+ <button id="remote-disable-no" class="btn" type="button">Keep enabled</button>
306
+ </div>
307
+ </div>
308
+ <details id="remote-details">
309
+ <summary>Connection details</summary>
310
+ <p id="remote-diagnostics" class="sub"></p>
311
+ <button id="remote-forget" class="btn" type="button" hidden>Leave the changed route in Tailscale</button>
312
+ </details>
313
+ <details id="remote-advanced">
314
+ <summary>Use another reverse proxy</summary>
315
+ <p id="remote-custom-note">Configure your proxy separately, then enter the hosts and browser origins it uses. Saving applies immediately and replaces environment settings.</p>
316
+ <form id="remote-custom-form">
317
+ <label for="remote-hosts">Allowed hosts, separated by commas</label>
318
+ <input id="remote-hosts" placeholder="guild.example.com" spellcheck="false" autocomplete="off" maxlength="16384">
319
+ <label for="remote-origins">Allowed origins, separated by commas</label>
320
+ <input id="remote-origins" placeholder="https://guild.example.com" spellcheck="false" autocomplete="off" maxlength="16384">
321
+ <button id="remote-custom-save" class="btn" type="submit">Save proxy settings</button>
322
+ </form>
323
+ </details>
324
+ </div>
325
+ </dialog>
326
+
153
327
  <dialog id="models" class="models" aria-labelledby="models-title">
154
328
  <div class="models-bar">
155
329
  <span id="models-icon" class="provider-icon small"></span>
@@ -204,19 +378,6 @@
204
378
  <p id="changelog-status" class="news-status" hidden></p>
205
379
  </dialog>
206
380
 
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
381
  <dialog id="history" class="models history" aria-labelledby="history-title">
221
382
  <div class="models-bar">
222
383
  <span id="history-icon" class="provider-icon small"></span>
@@ -241,54 +402,6 @@
241
402
  </form>
242
403
  </dialog>
243
404
 
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
405
  <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
406
 
294
407
  <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
+ }