[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
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:
136
domain/vaultOrder.ts
Normal file
136
domain/vaultOrder.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
export type VaultOrderPosition = "before" | "after";
|
||||
|
||||
export interface VaultOrderedItem {
|
||||
id: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
const ORDER_STEP = 1000;
|
||||
|
||||
const isFiniteOrder = (value: unknown): value is number =>
|
||||
typeof value === "number" && Number.isFinite(value);
|
||||
|
||||
export const getVaultOrderValue = <T extends { order?: number }>(
|
||||
item: T,
|
||||
fallbackIndex: number,
|
||||
): number => {
|
||||
return isFiniteOrder(item.order) ? item.order : (fallbackIndex + 1) * ORDER_STEP;
|
||||
};
|
||||
|
||||
export const normalizeVaultOrder = <T extends { order?: number }>(
|
||||
items: readonly T[],
|
||||
): T[] => {
|
||||
let changed = false;
|
||||
const usedOrders = new Set(
|
||||
items
|
||||
.map((item) => item.order)
|
||||
.filter(isFiniteOrder),
|
||||
);
|
||||
const next = items.map((item, index) => {
|
||||
if (isFiniteOrder(item.order)) return item;
|
||||
const order = (index + 1) * ORDER_STEP;
|
||||
let nextOrder = order;
|
||||
while (usedOrders.has(nextOrder)) {
|
||||
nextOrder += ORDER_STEP;
|
||||
}
|
||||
usedOrders.add(nextOrder);
|
||||
changed = true;
|
||||
return { ...item, order: nextOrder };
|
||||
});
|
||||
return changed ? next : [...items];
|
||||
};
|
||||
|
||||
export const renumberVaultOrder = <T extends { order?: number }>(
|
||||
items: readonly T[],
|
||||
): T[] => {
|
||||
return items.map((item, index) => {
|
||||
const order = (index + 1) * ORDER_STEP;
|
||||
return item.order === order ? item : { ...item, order };
|
||||
});
|
||||
};
|
||||
|
||||
export const sortByVaultOrder = <T extends { order?: number; label?: string; id?: string }>(
|
||||
items: readonly T[],
|
||||
): T[] => {
|
||||
const fallbackIndexByItem = new Map<T, number>();
|
||||
items.forEach((item, index) => fallbackIndexByItem.set(item, index));
|
||||
return [...items].sort((a, b) => {
|
||||
const orderDiff =
|
||||
getVaultOrderValue(a, fallbackIndexByItem.get(a) ?? 0) -
|
||||
getVaultOrderValue(b, fallbackIndexByItem.get(b) ?? 0);
|
||||
if (orderDiff !== 0) return orderDiff;
|
||||
const labelDiff = (a.label ?? "").localeCompare(b.label ?? "");
|
||||
if (labelDiff !== 0) return labelDiff;
|
||||
return (a.id ?? "").localeCompare(b.id ?? "");
|
||||
});
|
||||
};
|
||||
|
||||
export const reorderVaultItems = <T extends VaultOrderedItem>(
|
||||
items: readonly T[],
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
position: VaultOrderPosition,
|
||||
): T[] => {
|
||||
if (sourceId === targetId) return normalizeVaultOrder(items);
|
||||
|
||||
const ordered = sortByVaultOrder(items);
|
||||
const source = ordered.find((item) => item.id === sourceId);
|
||||
const target = ordered.find((item) => item.id === targetId);
|
||||
if (!source || !target) return normalizeVaultOrder(ordered);
|
||||
|
||||
const withoutSource = ordered.filter((item) => item.id !== sourceId);
|
||||
const targetIndex = withoutSource.findIndex((item) => item.id === targetId);
|
||||
if (targetIndex === -1) return normalizeVaultOrder(ordered);
|
||||
|
||||
const insertIndex = position === "before" ? targetIndex : targetIndex + 1;
|
||||
const next = [
|
||||
...withoutSource.slice(0, insertIndex),
|
||||
source,
|
||||
...withoutSource.slice(insertIndex),
|
||||
];
|
||||
|
||||
return renumberVaultOrder(next);
|
||||
};
|
||||
|
||||
export const getNextVaultOrder = <T extends { order?: number }>(
|
||||
items: readonly T[],
|
||||
): number => {
|
||||
const maxOrder = items.reduce((max, item, index) => {
|
||||
return Math.max(max, getVaultOrderValue(item, index));
|
||||
}, 0);
|
||||
return maxOrder + ORDER_STEP;
|
||||
};
|
||||
|
||||
export const sortVaultStringsByOrder = (
|
||||
values: readonly string[],
|
||||
orderByValue: ReadonlyMap<string, number>,
|
||||
): string[] => {
|
||||
return [...values].sort((a, b) => {
|
||||
const orderA = orderByValue.get(a);
|
||||
const orderB = orderByValue.get(b);
|
||||
if (isFiniteOrder(orderA) && isFiniteOrder(orderB) && orderA !== orderB) {
|
||||
return orderA - orderB;
|
||||
}
|
||||
if (isFiniteOrder(orderA)) return -1;
|
||||
if (isFiniteOrder(orderB)) return 1;
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
};
|
||||
|
||||
export const reorderVaultStrings = (
|
||||
values: readonly string[],
|
||||
source: string,
|
||||
target: string,
|
||||
position: VaultOrderPosition,
|
||||
): string[] => {
|
||||
if (source === target) return [...values];
|
||||
const withoutSource = values.filter((value) => value !== source);
|
||||
const targetIndex = withoutSource.indexOf(target);
|
||||
if (targetIndex === -1 || !values.includes(source)) return [...values];
|
||||
const insertIndex = position === "before" ? targetIndex : targetIndex + 1;
|
||||
return [
|
||||
...withoutSource.slice(0, insertIndex),
|
||||
source,
|
||||
...withoutSource.slice(insertIndex),
|
||||
];
|
||||
};
|
||||
Reference in New Issue
Block a user