[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
import { useCallback } from 'react';
import { hostTreeInlineGroupDeleteStore } from '../../application/state/hostTreeInlineGroupDeleteStore';
import { hostTreeInlineGroupEditStore } from '../../application/state/hostTreeInlineGroupEditStore';
import { hostTreeInlineHostEditStore } from '../../application/state/hostTreeInlineHostEditStore';
import {
allocateUnnamedGroupPath,
applyGroupPathRename,
ensureAncestorPathsExpanded,
groupDisplayName,
} from '../../domain/hostGroupPathMutations';
import type { Host, ManagedSource } from '../../types';
import { toast } from '../ui/toast';
type UseHostTreeInlineGroupActionsParams = {
customGroups: string[];
hosts: Host[];
managedSources: ManagedSource[];
onUpdateCustomGroups: (groups: string[]) => void;
onCommitGroupPathChange: (
sourcePath: string,
nextPath: string,
) => Promise<
| { ok: true }
| { ok: false; error?: string; superseded?: true }
>;
selectedGroupPath: string | null;
setSelectedGroupPath: (path: string | null) => void;
ensurePathExpanded: (path: string) => void;
unnamedGroupLabel: string;
t: (key: string) => string;
};
export function useHostTreeInlineGroupActions({
customGroups,
hosts,
managedSources,
onUpdateCustomGroups,
onCommitGroupPathChange,
selectedGroupPath,
setSelectedGroupPath,
ensurePathExpanded,
unnamedGroupLabel,
t,
}: UseHostTreeInlineGroupActionsParams) {
const startInlineNewGroup = useCallback((parentPath?: string) => {
hostTreeInlineHostEditStore.clear();
const parent = parentPath ?? null;
const { name, path } = allocateUnnamedGroupPath(customGroups, parent, unnamedGroupLabel);
onUpdateCustomGroups(Array.from(new Set([...customGroups, path])));
if (parent) {
ensureAncestorPathsExpanded(parent, ensurePathExpanded);
ensurePathExpanded(parent);
}
hostTreeInlineGroupEditStore.startEdit({
groupPath: path,
initialName: name,
isNew: true,
});
}, [customGroups, ensurePathExpanded, onUpdateCustomGroups, unnamedGroupLabel]);
const startInlineRenameGroup = useCallback((groupPath: string) => {
hostTreeInlineHostEditStore.clear();
hostTreeInlineGroupEditStore.startEdit({
groupPath,
initialName: groupDisplayName(groupPath),
isNew: false,
});
}, []);
const cancelInlineGroupEdit = useCallback(() => {
const edit = hostTreeInlineGroupEditStore.getEdit();
if (!edit) return;
if (edit.isNew) {
onUpdateCustomGroups(customGroups.filter((groupPath) => groupPath !== edit.groupPath));
}
hostTreeInlineGroupEditStore.clear();
}, [customGroups, onUpdateCustomGroups]);
const commitInlineGroupRename = useCallback(async (rawName: string): Promise<boolean> => {
const edit = hostTreeInlineGroupEditStore.getEdit();
if (!edit) return false;
const result = applyGroupPathRename({
renameTargetPath: edit.groupPath,
nextName: rawName,
customGroups,
hosts,
managedSources,
});
if (result.ok === false) {
if (result.error === 'unchanged') {
hostTreeInlineGroupEditStore.clear();
return true;
}
if (result.error === 'required') {
if (edit.isNew) {
cancelInlineGroupEdit();
return true;
}
toast.error(t('vault.groups.errors.required'));
return false;
}
if (result.error === 'invalidChars') {
toast.error(t('vault.groups.errors.invalidChars'));
return false;
}
if (result.error === 'duplicatePath') {
toast.error(t('vault.groups.errors.duplicatePath'));
return false;
}
return false;
}
const committed = await onCommitGroupPathChange(edit.groupPath, result.nextPath);
if (!committed.ok) {
toast.error(committed.error || t('common.error'));
return false;
}
if (
selectedGroupPath
&& (selectedGroupPath === edit.groupPath
|| selectedGroupPath.startsWith(`${edit.groupPath}/`))
) {
const suffix = selectedGroupPath === edit.groupPath
? ''
: selectedGroupPath.slice(edit.groupPath.length);
setSelectedGroupPath(result.nextPath + suffix);
}
hostTreeInlineGroupEditStore.clear();
return true;
}, [
cancelInlineGroupEdit,
customGroups,
hosts,
managedSources,
onCommitGroupPathChange,
selectedGroupPath,
setSelectedGroupPath,
t,
]);
const startInlineDeleteGroup = useCallback((groupPath: string) => {
hostTreeInlineGroupDeleteStore.open(groupPath);
}, []);
return {
startInlineNewGroup,
startInlineRenameGroup,
commitInlineGroupRename,
cancelInlineGroupEdit,
startInlineDeleteGroup,
};
}