@oddessentials/agent-guild 0.28.1 → 0.29.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/package.json +1 -1
- package/src/manager/folder-browser.mjs +39 -1
- package/src/manager/server.mjs +4 -0
- package/web/app.js +112 -46
- package/web/index.html +8 -1
- package/web/skins/gnomeland/skin.css +2 -2
- package/web/skins/goblinville/skin.css +2 -2
- package/web/skins/grove/skin.css +2 -2
- package/web/skins/guild/skin.css +1 -1
- package/web/skins/orbital/skin.css +2 -2
- package/web/styles.css +9 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oddessentials/agent-guild",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Launch and watch AI coding-assistant terminal sessions from one local web page. A bundled session manager owns the terminals so the page can close and reconnect.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,6 +16,20 @@ export function folderSegments(dir, paths = path) {
|
|
|
16
16
|
return segments;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
const WINDOWS_RESERVED = /^(con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(\..*)?$/i;
|
|
20
|
+
|
|
21
|
+
export function folderNameProblem(name, platform = process.platform) {
|
|
22
|
+
if (!name) return 'Enter a folder name.';
|
|
23
|
+
if (name === '.' || name === '..') return 'Choose a different folder name.';
|
|
24
|
+
if (/[/\\]/.test(name)) return 'A folder name cannot contain / or \\.';
|
|
25
|
+
if (/[\u0000-\u001f]/.test(name)) return 'A folder name cannot contain control characters.';
|
|
26
|
+
if (platform !== 'win32') return null;
|
|
27
|
+
if (/[<>:"|?*]/.test(name)) return 'A folder name cannot contain < > : " | ? or *.';
|
|
28
|
+
if (/[. ]$/.test(name)) return 'A folder name cannot end with a dot or a space.';
|
|
29
|
+
if (WINDOWS_RESERVED.test(name)) return `Windows reserves the name ${name}.`;
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
export function createFolderBrowser({ platform = process.platform, home = os.homedir(), limit = 2000, driveTimeoutMs = 500, fsp = fs.promises } = {}) {
|
|
20
34
|
const paths = platform === 'win32' ? path.win32 : path.posix;
|
|
21
35
|
|
|
@@ -59,7 +73,30 @@ export function createFolderBrowser({ platform = process.platform, home = os.hom
|
|
|
59
73
|
return folders.filter(Boolean).sort(byName);
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
|
|
76
|
+
const browser = {
|
|
77
|
+
async create(rawParent, rawName) {
|
|
78
|
+
if (typeof rawParent !== 'string' || !rawParent.trim() || rawParent.includes('\0')) throw failure(400, 'bad_path', 'The folder must be a path.');
|
|
79
|
+
const name = typeof rawName === 'string' ? rawName.trim() : '';
|
|
80
|
+
const problem = folderNameProblem(name, platform);
|
|
81
|
+
if (problem) throw failure(400, 'bad_name', problem);
|
|
82
|
+
const parent = paths.resolve(rawParent.trim());
|
|
83
|
+
const missing = () => failure(409, 'folder_missing', `${parent} no longer exists.`);
|
|
84
|
+
try {
|
|
85
|
+
if (!(await fsp.stat(parent)).isDirectory()) throw missing();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error.status) throw error;
|
|
88
|
+
throw error.code === 'ENOENT' || error.code === 'ENOTDIR' ? missing() : failure(409, 'folder_unreadable', `Agent Guild cannot read ${parent}.`);
|
|
89
|
+
}
|
|
90
|
+
const dir = paths.join(parent, name);
|
|
91
|
+
try {
|
|
92
|
+
await fsp.mkdir(dir);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error.code === 'EEXIST') throw failure(409, 'folder_exists', `${name} already exists in ${parent}.`);
|
|
95
|
+
if (error.code === 'ENOENT') throw missing();
|
|
96
|
+
throw failure(409, 'folder_unwritable', `Agent Guild cannot create ${dir}.`);
|
|
97
|
+
}
|
|
98
|
+
return browser.list(dir);
|
|
99
|
+
},
|
|
63
100
|
async list(raw) {
|
|
64
101
|
if (raw !== undefined && raw !== null && (typeof raw !== 'string' || raw.includes('\0'))) {
|
|
65
102
|
throw failure(400, 'bad_path', 'The folder must be a path.');
|
|
@@ -82,4 +119,5 @@ export function createFolderBrowser({ platform = process.platform, home = os.hom
|
|
|
82
119
|
};
|
|
83
120
|
},
|
|
84
121
|
};
|
|
122
|
+
return browser;
|
|
85
123
|
}
|
package/src/manager/server.mjs
CHANGED
|
@@ -287,6 +287,10 @@ export function createManagerServer({
|
|
|
287
287
|
if (route === '/folders' && method === 'GET') {
|
|
288
288
|
return sendJson(res, 200, await folderBrowser.list(url.searchParams.get('path') ?? undefined));
|
|
289
289
|
}
|
|
290
|
+
if (route === '/folders' && method === 'POST') {
|
|
291
|
+
const { path: parent, name } = await readJsonBody(req);
|
|
292
|
+
return sendJson(res, 201, await folderBrowser.create(parent, name));
|
|
293
|
+
}
|
|
290
294
|
if (route === '/upgrade' && method === 'POST') {
|
|
291
295
|
const session = await manager.upgrade();
|
|
292
296
|
return sendJson(res, 201, { session: session.toJSON() });
|
package/web/app.js
CHANGED
|
@@ -23,6 +23,7 @@ const GITHUB_ACCOUNT_KEY = 'agentGuild.githubAccount';
|
|
|
23
23
|
const CLONE_PARENT_KEY = 'agentGuild.cloneParent';
|
|
24
24
|
const HIDDEN_FOLDERS_KEY = 'agentGuild.showHiddenFolders';
|
|
25
25
|
const RECENT_CWDS_KEY = 'agentGuild.recentCwds';
|
|
26
|
+
const RECENT_CLONE_PARENTS_KEY = 'agentGuild.recentCloneParents';
|
|
26
27
|
const GITHUB_REPO_KEY = 'agentGuild.githubRepo';
|
|
27
28
|
const GITHUB_RECENT_KEY = 'agentGuild.githubRecent';
|
|
28
29
|
const GITHUB_VIEW_KEY = 'agentGuild.githubView';
|
|
@@ -767,68 +768,78 @@ async function openWorkingFolder() {
|
|
|
767
768
|
}
|
|
768
769
|
}
|
|
769
770
|
|
|
770
|
-
// ---- recent
|
|
771
|
+
// ---- recent folders ---------------------------------------------------------
|
|
771
772
|
|
|
772
|
-
const
|
|
773
|
+
const RECENT_FIELDS = {
|
|
774
|
+
cwd: { input: 'cwd', list: 'cwd-recent', key: RECENT_CWDS_KEY, pick: (dir) => useFolder(dir) },
|
|
775
|
+
clone: { input: 'github-parent', list: 'github-parent-recent', key: RECENT_CLONE_PARENTS_KEY, pick: (dir) => setCloneParent(dir) },
|
|
776
|
+
};
|
|
777
|
+
const recentView = { field: null, typed: false, active: -1 };
|
|
773
778
|
|
|
774
|
-
function
|
|
775
|
-
return readRecentFolders(load(
|
|
779
|
+
function recentFolders(field) {
|
|
780
|
+
return readRecentFolders(load(RECENT_FIELDS[field].key));
|
|
776
781
|
}
|
|
777
782
|
|
|
778
|
-
function
|
|
779
|
-
if (dir) save(
|
|
783
|
+
function rememberRecent(field, dir) {
|
|
784
|
+
if (dir) save(RECENT_FIELDS[field].key, JSON.stringify(rememberFolder(recentFolders(field), dir, { caseless: state.platform === 'win32' })));
|
|
780
785
|
}
|
|
781
786
|
|
|
782
|
-
function
|
|
783
|
-
const input
|
|
784
|
-
const
|
|
785
|
-
|
|
787
|
+
function renderRecent(field) {
|
|
788
|
+
const { input: inputId, list: listId } = RECENT_FIELDS[field];
|
|
789
|
+
const input = $(inputId), list = $(listId);
|
|
790
|
+
const open = recentView.field === field;
|
|
791
|
+
const shown = open ? matchFolders(recentFolders(field), recentView.typed ? input.value : '') : [];
|
|
792
|
+
if (open) recentView.active = Math.min(recentView.active, shown.length - 1);
|
|
786
793
|
list.replaceChildren(...shown.map((dir, i) => {
|
|
787
794
|
const option = el('li', 'cwd-recent-option', dir);
|
|
788
|
-
option.id =
|
|
795
|
+
option.id = `${listId}-${i}`;
|
|
789
796
|
option.setAttribute('role', 'option');
|
|
790
|
-
option.setAttribute('aria-selected', String(i === recentView.active));
|
|
797
|
+
option.setAttribute('aria-selected', String(open && i === recentView.active));
|
|
791
798
|
option.addEventListener('pointerdown', (e) => e.preventDefault());
|
|
792
|
-
option.addEventListener('click', () =>
|
|
799
|
+
option.addEventListener('click', () => pickRecent(field, dir));
|
|
793
800
|
return option;
|
|
794
801
|
}));
|
|
795
802
|
list.hidden = !shown.length;
|
|
796
803
|
input.setAttribute('aria-expanded', String(!list.hidden));
|
|
797
|
-
if (recentView.active >= 0) input.setAttribute('aria-activedescendant',
|
|
804
|
+
if (open && recentView.active >= 0) input.setAttribute('aria-activedescendant', `${listId}-${recentView.active}`);
|
|
798
805
|
else input.removeAttribute('aria-activedescendant');
|
|
799
806
|
}
|
|
800
807
|
|
|
801
|
-
function
|
|
802
|
-
|
|
803
|
-
|
|
808
|
+
function showRecent(field, typed = false) {
|
|
809
|
+
const previous = recentView.field;
|
|
810
|
+
Object.assign(recentView, { field, typed, active: -1 });
|
|
811
|
+
if (previous && previous !== field) renderRecent(previous);
|
|
812
|
+
renderRecent(field);
|
|
804
813
|
}
|
|
805
814
|
|
|
806
|
-
function
|
|
807
|
-
|
|
808
|
-
|
|
815
|
+
function hideRecent(field) {
|
|
816
|
+
if (recentView.field !== field) return;
|
|
817
|
+
Object.assign(recentView, { field: null, active: -1 });
|
|
818
|
+
renderRecent(field);
|
|
809
819
|
}
|
|
810
820
|
|
|
811
|
-
function
|
|
812
|
-
|
|
813
|
-
|
|
821
|
+
function pickRecent(field, dir) {
|
|
822
|
+
hideRecent(field);
|
|
823
|
+
RECENT_FIELDS[field].pick(dir);
|
|
814
824
|
}
|
|
815
825
|
|
|
816
|
-
function
|
|
817
|
-
const
|
|
826
|
+
function recentKeys(field, e) {
|
|
827
|
+
const list = $(RECENT_FIELDS[field].list);
|
|
828
|
+
const options = list.hidden ? [] : [...list.children];
|
|
818
829
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
819
830
|
e.preventDefault();
|
|
820
|
-
if (
|
|
821
|
-
const count =
|
|
831
|
+
if (recentView.field !== field) showRecent(field);
|
|
832
|
+
const count = list.children.length;
|
|
822
833
|
if (!count) return;
|
|
823
834
|
recentView.active = e.key === 'ArrowDown' ? (recentView.active + 1) % count : recentView.active <= 0 ? count - 1 : recentView.active - 1;
|
|
824
|
-
|
|
825
|
-
$(
|
|
835
|
+
renderRecent(field);
|
|
836
|
+
$(`${RECENT_FIELDS[field].list}-${recentView.active}`).scrollIntoView?.({ block: 'nearest' });
|
|
826
837
|
} else if (e.key === 'Enter' && recentView.active >= 0 && options[recentView.active]) {
|
|
827
838
|
e.preventDefault();
|
|
828
|
-
|
|
839
|
+
pickRecent(field, options[recentView.active].textContent);
|
|
829
840
|
} else if (e.key === 'Escape' && options.length) {
|
|
830
841
|
e.preventDefault();
|
|
831
|
-
|
|
842
|
+
hideRecent(field);
|
|
832
843
|
}
|
|
833
844
|
}
|
|
834
845
|
|
|
@@ -854,7 +865,52 @@ function chooseFolder(field) {
|
|
|
854
865
|
browseTo($(FOLDER_FIELDS[field].input).value.trim());
|
|
855
866
|
}
|
|
856
867
|
|
|
868
|
+
function showNewFolder(open) {
|
|
869
|
+
$('folder-new').hidden = !open;
|
|
870
|
+
$('folder-new-open').setAttribute('aria-expanded', String(open));
|
|
871
|
+
if (open) $('folder-new-name').value = '';
|
|
872
|
+
renderFolderBrowser();
|
|
873
|
+
(open ? $('folder-new-name') : $('folder-new-open')).focus();
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
async function createFolder(e) {
|
|
877
|
+
e?.preventDefault();
|
|
878
|
+
const parent = folderView.listing?.path;
|
|
879
|
+
const name = $('folder-new-name').value.trim();
|
|
880
|
+
if (!parent || !name || folderView.loading) return;
|
|
881
|
+
const request = ++folderView.request;
|
|
882
|
+
folderView.loading = true;
|
|
883
|
+
folderView.error = null;
|
|
884
|
+
renderFolderBrowser();
|
|
885
|
+
try {
|
|
886
|
+
const listing = await api('POST', '/folders', { path: parent, name });
|
|
887
|
+
if (request !== folderView.request) return;
|
|
888
|
+
folderView.listing = listing;
|
|
889
|
+
$('folder-filter').value = '';
|
|
890
|
+
$('folder-new').hidden = true;
|
|
891
|
+
$('folder-new-open').setAttribute('aria-expanded', 'false');
|
|
892
|
+
} catch (err) {
|
|
893
|
+
if (request !== folderView.request) return;
|
|
894
|
+
if (err instanceof AuthError) {
|
|
895
|
+
$('folder-browser').close();
|
|
896
|
+
return showAuth(err.message);
|
|
897
|
+
}
|
|
898
|
+
folderView.error = err.message || 'Could not create the folder.';
|
|
899
|
+
}
|
|
900
|
+
folderView.loading = false;
|
|
901
|
+
renderFolderBrowser();
|
|
902
|
+
if ($('folder-browser').open) ($('folder-new').hidden ? $('folder-use') : $('folder-new-name')).focus();
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function newFolderKeys(e) {
|
|
906
|
+
if (e.key !== 'Escape') return;
|
|
907
|
+
e.preventDefault();
|
|
908
|
+
showNewFolder(false);
|
|
909
|
+
}
|
|
910
|
+
|
|
857
911
|
async function browseTo(dir) {
|
|
912
|
+
$('folder-new').hidden = true;
|
|
913
|
+
$('folder-new-open').setAttribute('aria-expanded', 'false');
|
|
858
914
|
const request = ++folderView.request;
|
|
859
915
|
folderView.loading = true;
|
|
860
916
|
folderView.error = null;
|
|
@@ -908,6 +964,8 @@ function renderFolderBrowser() {
|
|
|
908
964
|
status.classList.toggle('error', Boolean(error));
|
|
909
965
|
$('folder-current').textContent = listing?.path ?? '';
|
|
910
966
|
$('folder-use').disabled = loading || !listing;
|
|
967
|
+
$('folder-new-open').disabled = loading || !listing;
|
|
968
|
+
$('folder-new-create').disabled = loading || !listing || !$('folder-new-name').value.trim();
|
|
911
969
|
}
|
|
912
970
|
|
|
913
971
|
function folderBrowserClosed() {
|
|
@@ -925,11 +983,9 @@ function useBrowsedFolder() {
|
|
|
925
983
|
if (!dir || folderView.loading) return;
|
|
926
984
|
const field = folderView.field;
|
|
927
985
|
$('folder-browser').close();
|
|
986
|
+
rememberRecent(field, dir);
|
|
928
987
|
if (field === 'clone') setCloneParent(dir);
|
|
929
|
-
else
|
|
930
|
-
rememberCwd(dir);
|
|
931
|
-
useFolder(dir);
|
|
932
|
-
}
|
|
988
|
+
else useFolder(dir);
|
|
933
989
|
}
|
|
934
990
|
|
|
935
991
|
function moveInFolders(e) {
|
|
@@ -2418,7 +2474,7 @@ async function startSession(provider, card, { resume, cwd, account = selectedAcc
|
|
|
2418
2474
|
({ session } = await api('POST', '/sessions', { ...body, cwd: working || undefined }));
|
|
2419
2475
|
}
|
|
2420
2476
|
upsertSession(session);
|
|
2421
|
-
|
|
2477
|
+
rememberRecent('cwd', session.cwd);
|
|
2422
2478
|
closeHistory({ focusOpener: false });
|
|
2423
2479
|
openPanel(session.id);
|
|
2424
2480
|
} catch (err) {
|
|
@@ -2628,7 +2684,7 @@ const GITHUB_SCOPES = {
|
|
|
2628
2684
|
repo: 'Read and write access to your repositories, private ones included, so you can browse repositories and create or edit issues.',
|
|
2629
2685
|
'write:public_key': 'Add the SSH key Agent Guild creates for this account.',
|
|
2630
2686
|
};
|
|
2631
|
-
const githubView = { accountId: null, repos: null, reposFor: null, loading: null, error: null, parentError: null, card: null, started: new
|
|
2687
|
+
const githubView = { accountId: null, repos: null, reposFor: null, loading: null, error: null, parentError: null, card: null, started: new Map(), announced: new Set() };
|
|
2632
2688
|
let githubLoading = null;
|
|
2633
2689
|
|
|
2634
2690
|
function el(tag, className, ...children) {
|
|
@@ -3093,8 +3149,9 @@ function renderGitHubRepos(github, account, repos) {
|
|
|
3093
3149
|
}
|
|
3094
3150
|
|
|
3095
3151
|
async function startClone(account, fullName) {
|
|
3096
|
-
const
|
|
3097
|
-
|
|
3152
|
+
const parent = cloneParent();
|
|
3153
|
+
const { session } = await api('POST', '/github/clone', { account: account.id, repo: fullName, parent });
|
|
3154
|
+
githubView.started.set(session.id, parent);
|
|
3098
3155
|
upsertSession(session);
|
|
3099
3156
|
openPanel(session.id);
|
|
3100
3157
|
}
|
|
@@ -3204,8 +3261,10 @@ function noticeClone(s) {
|
|
|
3204
3261
|
githubPick.origins.clear();
|
|
3205
3262
|
if (dockShows('github')) loadRepos();
|
|
3206
3263
|
if (!githubView.started.has(s.id)) return;
|
|
3207
|
-
if (clonedPath(s))
|
|
3208
|
-
|
|
3264
|
+
if (clonedPath(s)) {
|
|
3265
|
+
rememberRecent('clone', githubView.started.get(s.id));
|
|
3266
|
+
toast(`Cloned ${s.clone.repo} into ${s.clone.path}.`, 12000, { label: 'Use folder', run: () => useFolder(s.clone.path) });
|
|
3267
|
+
} else toast(`Cloning ${s.clone?.repo ?? 'the repository'} did not finish. Its session shows why.`, 8000);
|
|
3209
3268
|
}
|
|
3210
3269
|
|
|
3211
3270
|
function useFolder(dir) {
|
|
@@ -5587,11 +5646,13 @@ $('panel-stop').addEventListener('click', () => {
|
|
|
5587
5646
|
});
|
|
5588
5647
|
$('cwd').value = load(CWD_KEY) || '';
|
|
5589
5648
|
$('cwd-pick').addEventListener('click', firstClick(() => chooseFolder('cwd')));
|
|
5590
|
-
|
|
5591
|
-
$(
|
|
5592
|
-
$(
|
|
5593
|
-
$(
|
|
5594
|
-
$(
|
|
5649
|
+
for (const [field, { input }] of Object.entries(RECENT_FIELDS)) {
|
|
5650
|
+
$(input).addEventListener('focus', () => showRecent(field));
|
|
5651
|
+
$(input).addEventListener('click', () => { if (recentView.field !== field) showRecent(field); });
|
|
5652
|
+
$(input).addEventListener('input', () => showRecent(field, true));
|
|
5653
|
+
$(input).addEventListener('blur', () => hideRecent(field));
|
|
5654
|
+
$(input).addEventListener('keydown', (e) => recentKeys(field, e));
|
|
5655
|
+
}
|
|
5595
5656
|
$('folder-cancel').addEventListener('click', () => $('folder-browser').close());
|
|
5596
5657
|
$('folder-browser').addEventListener('click', (e) => { if (e.target === $('folder-browser')) $('folder-browser').close(); });
|
|
5597
5658
|
$('folder-browser').addEventListener('close', folderBrowserClosed);
|
|
@@ -5605,6 +5666,11 @@ $('folder-hidden').addEventListener('change', (e) => {
|
|
|
5605
5666
|
renderFolderBrowser();
|
|
5606
5667
|
});
|
|
5607
5668
|
$('folder-use').addEventListener('click', useBrowsedFolder);
|
|
5669
|
+
$('folder-new-open').addEventListener('click', () => showNewFolder($('folder-new').hidden));
|
|
5670
|
+
$('folder-new-cancel').addEventListener('click', () => showNewFolder(false));
|
|
5671
|
+
$('folder-new').addEventListener('submit', createFolder);
|
|
5672
|
+
$('folder-new-name').addEventListener('input', renderFolderBrowser);
|
|
5673
|
+
$('folder-new-name').addEventListener('keydown', newFolderKeys);
|
|
5608
5674
|
$('cwd-open').addEventListener('click', firstClick(openWorkingFolder));
|
|
5609
5675
|
try { state.accounts = JSON.parse(load(ACCOUNTS_KEY)) || {}; } catch { state.accounts = {}; }
|
|
5610
5676
|
try { state.shellPicks = JSON.parse(load(SHELLS_KEY)) || {}; } catch { state.shellPicks = {}; }
|
package/web/index.html
CHANGED
|
@@ -244,7 +244,8 @@
|
|
|
244
244
|
<div id="github-clone-into" class="history-form github-form">
|
|
245
245
|
<label for="github-parent">Clone into</label>
|
|
246
246
|
<div class="cwd-field single">
|
|
247
|
-
<input id="github-parent" type="text" placeholder="Folder that holds your clones" spellcheck="false" autocomplete="off">
|
|
247
|
+
<input id="github-parent" type="text" placeholder="Folder that holds your clones" spellcheck="false" autocomplete="off" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="github-parent-recent">
|
|
248
|
+
<ul id="github-parent-recent" class="cwd-recent above" role="listbox" aria-label="Recent clone folders" hidden></ul>
|
|
248
249
|
<div class="cwd-tools">
|
|
249
250
|
<button id="github-parent-pick" class="cwd-tool" type="button" aria-label="Choose a folder…" title="Connect to the session manager to choose folders" disabled>
|
|
250
251
|
<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">
|
|
@@ -434,7 +435,13 @@
|
|
|
434
435
|
<button id="folder-up" class="btn" type="button">Up</button>
|
|
435
436
|
<button id="folder-home" class="btn folder-chip" type="button">Home</button>
|
|
436
437
|
<div id="folder-roots" class="folder-roots"></div>
|
|
438
|
+
<button id="folder-new-open" class="btn folder-new-open" type="button" aria-expanded="false" aria-controls="folder-new">New folder</button>
|
|
437
439
|
</div>
|
|
440
|
+
<form id="folder-new" class="history-tools folder-new" hidden>
|
|
441
|
+
<input id="folder-new-name" type="text" placeholder="New folder name" aria-label="New folder name" spellcheck="false" autocomplete="off" autocapitalize="off" enterkeyhint="done">
|
|
442
|
+
<button id="folder-new-create" class="btn primary" type="submit">Create</button>
|
|
443
|
+
<button id="folder-new-cancel" class="btn" type="button">Cancel</button>
|
|
444
|
+
</form>
|
|
438
445
|
<div class="history-tools">
|
|
439
446
|
<input id="folder-filter" type="search" placeholder="Filter folders" aria-label="Filter folders" spellcheck="false" autocomplete="off">
|
|
440
447
|
<label class="history-here"><input id="folder-hidden" type="checkbox"><span>Show hidden</span></label>
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
}
|
|
244
244
|
.btn.primary:hover { filter: brightness(1.08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 0 0 1px rgba(233, 196, 122, 0.6), 0 5px 16px rgba(40, 70, 30, 0.35); }
|
|
245
245
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
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); }
|
|
246
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--panel-strong); }
|
|
247
247
|
|
|
248
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); }
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
.btn.primary { border-color: #6a8f3a; background: linear-gradient(#55924a, #35662f); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 0 0 1px rgba(233, 196, 122, 0.3), 0 0 16px rgba(110, 170, 80, 0.3); }
|
|
363
363
|
.btn.danger { color: #f59a8c; }
|
|
364
364
|
.btn.danger:hover { border-color: #b8402f; box-shadow: 0 0 12px rgba(240, 123, 106, 0.3); }
|
|
365
|
-
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #4a3824; background: rgba(10, 7, 4, 0.7); }
|
|
365
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #4a3824; background: rgba(10, 7, 4, 0.7); }
|
|
366
366
|
|
|
367
367
|
.models::backdrop, .news-panel::backdrop { background: rgba(6, 4, 2, 0.66); }
|
|
368
368
|
.terminal-panel { border-color: #5a4226; box-shadow: 0 0 0 1px rgba(233, 196, 122, 0.06), 0 30px 80px rgba(0, 0, 0, 0.7); }
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
}
|
|
250
250
|
.btn.primary:hover { filter: brightness(1.08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 0 0 1px rgba(227, 166, 119, 0.6), 0 5px 16px rgba(20, 70, 56, 0.35); }
|
|
251
251
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
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); }
|
|
252
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--panel-strong); }
|
|
253
253
|
|
|
254
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); }
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
.btn.primary { border-color: #3f9a80; background: linear-gradient(#2f8a73, #1b5a4b); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 0 0 1px rgba(227, 166, 119, 0.3), 0 0 16px rgba(60, 170, 140, 0.3); }
|
|
370
370
|
.btn.danger { color: #f59a8c; }
|
|
371
371
|
.btn.danger:hover { border-color: #b8402f; box-shadow: 0 0 12px rgba(240, 123, 106, 0.3); }
|
|
372
|
-
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #3a4a3f; background: rgba(5, 8, 6, 0.7); }
|
|
372
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #3a4a3f; background: rgba(5, 8, 6, 0.7); }
|
|
373
373
|
|
|
374
374
|
.models::backdrop, .news-panel::backdrop { background: rgba(2, 5, 3, 0.66); }
|
|
375
375
|
.terminal-panel { border-color: #5a3a22; box-shadow: 0 0 0 1px rgba(227, 166, 119, 0.06), 0 30px 80px rgba(0, 0, 0, 0.7); }
|
package/web/skins/grove/skin.css
CHANGED
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
}
|
|
245
245
|
.btn.primary:hover { filter: brightness(1.07); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 6px 20px rgba(45, 107, 69, 0.36); }
|
|
246
246
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
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); }
|
|
247
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--paper-strong); }
|
|
248
248
|
|
|
249
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); }
|
|
@@ -371,7 +371,7 @@
|
|
|
371
371
|
.btn.primary { background: linear-gradient(135deg, #4a8c5e, #2a6340); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 18px rgba(80, 160, 105, 0.35); }
|
|
372
372
|
.btn.danger { color: #ff9a8e; }
|
|
373
373
|
.btn.danger:hover { border-color: #b8443a; box-shadow: 0 0 12px rgba(240, 122, 108, 0.3); }
|
|
374
|
-
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #2e4a3d; background: rgba(4, 10, 8, 0.7); }
|
|
374
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #2e4a3d; background: rgba(4, 10, 8, 0.7); }
|
|
375
375
|
|
|
376
376
|
.models::backdrop, .news-panel::backdrop { background: rgba(2, 6, 5, 0.66); }
|
|
377
377
|
.terminal-panel { border-color: #2e4a3d; box-shadow: 0 0 0 1px rgba(140, 210, 160, 0.06), 0 30px 80px rgba(0, 0, 0, 0.7); }
|
package/web/skins/guild/skin.css
CHANGED
|
@@ -480,7 +480,7 @@
|
|
|
480
480
|
}
|
|
481
481
|
.btn.primary:hover { border-color: #b8892f; filter: brightness(1.08); }
|
|
482
482
|
.btn.danger:hover { border-color: #c4625d; }
|
|
483
|
-
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .notes-text {
|
|
483
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .notes-text {
|
|
484
484
|
border-color: #dccba5; background: #fffdf8; box-shadow: inset 0 1px 2px rgba(70, 48, 12, 0.06);
|
|
485
485
|
}
|
|
486
486
|
.auth { border-color: #e4d6b6; box-shadow: var(--card-shadow); }
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
}
|
|
247
247
|
.btn.primary:hover { filter: brightness(1.08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 6px 20px rgba(122, 79, 232, 0.4); }
|
|
248
248
|
.btn.danger:hover { border-color: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent); }
|
|
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); }
|
|
249
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { background: var(--glass-strong); }
|
|
250
250
|
|
|
251
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); }
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
.btn.primary { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 0 18px rgba(106, 92, 240, 0.45); }
|
|
368
368
|
.btn.danger { color: #ff8c9c; }
|
|
369
369
|
.btn.danger:hover { border-color: #c23a52; box-shadow: 0 0 12px rgba(255, 107, 129, 0.3); }
|
|
370
|
-
.cwd input, .auth input, .history-tools input[type="search"], .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #26355c; background: rgba(4, 7, 16, 0.7); }
|
|
370
|
+
.cwd input, .auth input, .history-tools input[type="search"], .folder-new input, .history-form input, .github-fields input, .github-fields select, .notes-text { border-color: #26355c; background: rgba(4, 7, 16, 0.7); }
|
|
371
371
|
|
|
372
372
|
.models::backdrop, .news-panel::backdrop { background: rgba(2, 4, 10, 0.66); }
|
|
373
373
|
.terminal-panel { border-color: #2a3a66; box-shadow: 0 0 0 1px rgba(120, 170, 255, 0.06), 0 30px 80px rgba(0, 0, 0, 0.7); }
|
package/web/styles.css
CHANGED
|
@@ -749,7 +749,7 @@ a.news-link:visited { color: var(--muted); }
|
|
|
749
749
|
}
|
|
750
750
|
|
|
751
751
|
.history-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 14px 0; }
|
|
752
|
-
.history-tools input[type="search"] {
|
|
752
|
+
.history-tools input[type="search"], .folder-new input {
|
|
753
753
|
flex: 1 1 220px; min-width: 0; padding: 7px 10px; border-radius: var(--radius); border: 1px solid var(--border);
|
|
754
754
|
background: var(--surface); color: var(--text); font: inherit; font-size: 0.9rem;
|
|
755
755
|
}
|
|
@@ -1035,6 +1035,9 @@ a.btn.github-out::after {
|
|
|
1035
1035
|
.folder-crumb:focus-visible, .folder-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
|
|
1036
1036
|
.folder-places, .folder-roots { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
1037
1037
|
.folder-places .btn { min-height: 36px; }
|
|
1038
|
+
.folder-new-open { margin-left: auto; }
|
|
1039
|
+
.folder-new { flex-wrap: nowrap; }
|
|
1040
|
+
.folder-new input { flex: 1; min-width: 0; }
|
|
1038
1041
|
.folder-list { display: flex; flex-direction: column; }
|
|
1039
1042
|
.folder-row {
|
|
1040
1043
|
display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px; border: 0; border-bottom: 1px solid var(--border);
|
|
@@ -1047,6 +1050,9 @@ a.btn.github-out::after {
|
|
|
1047
1050
|
.folder-footer { justify-content: space-between; }
|
|
1048
1051
|
.folder-current { flex: 1; min-width: 0; font-family: ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace; font-size: 0.8rem; overflow-wrap: anywhere; }
|
|
1049
1052
|
.folder-footer .btn { min-height: 42px; }
|
|
1053
|
+
@media (pointer: coarse), (max-width: 640px) {
|
|
1054
|
+
.folder-places .btn, .folder-new .btn, .folder-new input, .folder-footer .btn, #folder-cancel { min-height: 44px; }
|
|
1055
|
+
}
|
|
1050
1056
|
.cwd-recent {
|
|
1051
1057
|
position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 5; margin: 0; padding: 4px; list-style: none;
|
|
1052
1058
|
border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
|
|
@@ -1056,4 +1062,5 @@ a.btn.github-out::after {
|
|
|
1056
1062
|
color: var(--text); font-family: ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace; font-size: 0.82rem; overflow-wrap: anywhere; cursor: pointer;
|
|
1057
1063
|
}
|
|
1058
1064
|
.cwd-recent-option:hover, .cwd-recent-option[aria-selected="true"] { background: color-mix(in srgb, var(--accent) 14%, transparent); }
|
|
1059
|
-
@media (pointer: coarse) { .cwd-recent-option { min-height: 46px; } }
|
|
1065
|
+
@media (pointer: coarse), (max-width: 640px) { .cwd-recent-option { min-height: 46px; } }
|
|
1066
|
+
.cwd-recent.above { top: auto; bottom: calc(100% + 4px); }
|