59533b997b
- Artifacts: folders are rounded chips with a count badge; rename/delete are icons beside the picked folder (double-click renames); New folder is a dashed chip; the empty-folder hint mentions drag and drop. - GET /api/pricing/quota: every Claude row carries its accountUuid. - Settings → Profiles and the status bar's Usage panel: a profile hidden in this browser is left out of the Usage panel too; when the live login is hidden, the first visible profile is shown in its place (Logged in now, "in use", the % used chip with that profile's own usage). Display only.
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
/**
|
|
* @file hiddenProfilesStorage.ts
|
|
* @description Claude login profiles this browser leaves out of Settings →
|
|
* AI tools → Profiles, kept in localStorage with the same read/validate/
|
|
* fallback convention as laneOrderStorage.ts. A viewer preference only: the
|
|
* profile files, the API and the database are untouched, and another browser
|
|
* still lists every profile. The status bar's Usage panel reads the same list
|
|
* (`HIDDEN_PROFILES_EVENT` tells it of a change in this tab), and when the
|
|
* live login is hidden both show the first visible profile in its place
|
|
* (`standIn`).
|
|
*
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const STORAGE_KEY = "agenthub.cc.hiddenProfiles";
|
|
/** Fired on `window` after this tab changes the list. */
|
|
export const HIDDEN_PROFILES_EVENT = "agenthub:hidden-profiles";
|
|
|
|
/** A profile's stable key: its account (survives a rename), else its file name. */
|
|
export function profileKey(p: { accountUuid: string | null; name?: string }): string {
|
|
return p.accountUuid || p.name || "";
|
|
}
|
|
|
|
export function readHiddenProfiles(): string[] {
|
|
try {
|
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
if (!raw) return [];
|
|
const parsed: unknown = JSON.parse(raw);
|
|
if (!Array.isArray(parsed)) return [];
|
|
return parsed.filter((k): k is string => typeof k === "string" && k.length > 0);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export function writeHiddenProfiles(keys: string[]): void {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(keys));
|
|
} catch {
|
|
/* ignore quota / disabled storage */
|
|
}
|
|
try {
|
|
window.dispatchEvent(new Event(HIDDEN_PROFILES_EVENT));
|
|
} catch {
|
|
/* no window (tests outside jsdom) */
|
|
}
|
|
}
|
|
|
|
/** True when `key` names a change to the hidden list (a `storage` event's key). */
|
|
export const isHiddenProfilesKey = (key: string | null) => key === STORAGE_KEY || key === null;
|
|
|
|
/**
|
|
* The entry shown in place of a hidden live login: the first one in `list`
|
|
* that is not hidden, or null when the live one is visible (or none is left).
|
|
*/
|
|
export function standIn<T extends { accountUuid?: string | null; name?: string }>(
|
|
list: T[],
|
|
isActive: (x: T) => boolean,
|
|
hidden: string[]
|
|
): T | null {
|
|
const live = list.find(isActive);
|
|
if (
|
|
!live ||
|
|
!hidden.includes(profileKey({ accountUuid: live.accountUuid ?? null, name: live.name }))
|
|
)
|
|
return null;
|
|
return (
|
|
list.find(
|
|
(x) =>
|
|
!isActive(x) &&
|
|
!hidden.includes(profileKey({ accountUuid: x.accountUuid ?? null, name: x.name }))
|
|
) ?? null
|
|
);
|
|
}
|
|
|
|
/** `keys` with `key` added when absent, removed when present. */
|
|
export function toggleHidden(keys: string[], key: string): string[] {
|
|
return keys.includes(key) ? keys.filter((k) => k !== key) : [...keys, key];
|
|
}
|