[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:
158
application/state/useToolbarItemLayout.ts
Normal file
158
application/state/useToolbarItemLayout.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
moveToolbarItem,
|
||||
normalizeToolbarItemLayout,
|
||||
partitionToolbarItems,
|
||||
reorderToolbarItems,
|
||||
resetToolbarItemLayout,
|
||||
setToolbarItemPlacement,
|
||||
type ToolbarItemLayout,
|
||||
type ToolbarItemLayoutDefaults,
|
||||
type ToolbarItemPartition,
|
||||
type ToolbarItemPlacement,
|
||||
} from '../../domain/toolbarItemLayout';
|
||||
import {
|
||||
LOCAL_STORAGE_ADAPTER_CHANGED_EVENT,
|
||||
localStorageAdapter,
|
||||
} from '../../infrastructure/persistence/localStorageAdapter';
|
||||
|
||||
export type UseToolbarItemLayoutResult = {
|
||||
layout: ToolbarItemLayout;
|
||||
partition: (availableIds?: readonly string[] | ReadonlySet<string>) => ToolbarItemPartition;
|
||||
setPlacement: (
|
||||
id: string,
|
||||
placement: ToolbarItemPlacement,
|
||||
availableIds?: readonly string[] | ReadonlySet<string> | null,
|
||||
) => ToolbarItemLayout;
|
||||
/** Replace full order while preserving placements (normalized against defaults). */
|
||||
setOrder: (order: string[]) => void;
|
||||
reorder: (draggedId: string, targetId: string, placement?: 'before' | 'after') => void;
|
||||
move: (
|
||||
id: string,
|
||||
direction: 'earlier' | 'later',
|
||||
availableIds?: readonly string[] | ReadonlySet<string> | null,
|
||||
) => void;
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
function readLayout(storageKey: string, defaults: ToolbarItemLayoutDefaults): ToolbarItemLayout {
|
||||
try {
|
||||
return normalizeToolbarItemLayout(localStorageAdapter.read(storageKey), defaults);
|
||||
} catch {
|
||||
return resetToolbarItemLayout(defaults);
|
||||
}
|
||||
}
|
||||
|
||||
function writeLayout(storageKey: string, layout: ToolbarItemLayout): void {
|
||||
try {
|
||||
localStorageAdapter.write(storageKey, layout);
|
||||
} catch {
|
||||
// Best effort; in-memory state still applies for the session.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist show / collapse / hide + order for a dense toolbar region.
|
||||
* `defaults` should be a stable reference (module-level const).
|
||||
*/
|
||||
export function useToolbarItemLayout(
|
||||
storageKey: string,
|
||||
defaults: ToolbarItemLayoutDefaults,
|
||||
): UseToolbarItemLayoutResult {
|
||||
const [layout, setLayout] = useState<ToolbarItemLayout>(() => readLayout(storageKey, defaults));
|
||||
|
||||
useEffect(() => {
|
||||
const sync = (event: Event) => {
|
||||
if (event instanceof StorageEvent && event.key !== storageKey) return;
|
||||
if (event instanceof CustomEvent && event.detail?.key !== storageKey) return;
|
||||
setLayout(readLayout(storageKey, defaults));
|
||||
};
|
||||
window.addEventListener('storage', sync);
|
||||
window.addEventListener(LOCAL_STORAGE_ADAPTER_CHANGED_EVENT, sync);
|
||||
return () => {
|
||||
window.removeEventListener('storage', sync);
|
||||
window.removeEventListener(LOCAL_STORAGE_ADAPTER_CHANGED_EVENT, sync);
|
||||
};
|
||||
}, [defaults, storageKey]);
|
||||
|
||||
const commit = useCallback(
|
||||
(next: ToolbarItemLayout) => {
|
||||
setLayout(next);
|
||||
writeLayout(storageKey, next);
|
||||
},
|
||||
[storageKey],
|
||||
);
|
||||
|
||||
const setPlacement = useCallback(
|
||||
(
|
||||
id: string,
|
||||
placement: ToolbarItemPlacement,
|
||||
availableIds?: readonly string[] | ReadonlySet<string> | null,
|
||||
): ToolbarItemLayout => {
|
||||
let nextLayout!: ToolbarItemLayout;
|
||||
setLayout((current) => {
|
||||
nextLayout = setToolbarItemPlacement(current, id, placement, defaults, availableIds);
|
||||
writeLayout(storageKey, nextLayout);
|
||||
return nextLayout;
|
||||
});
|
||||
return nextLayout;
|
||||
},
|
||||
[defaults, storageKey],
|
||||
);
|
||||
|
||||
const setOrder = useCallback(
|
||||
(order: string[]) => {
|
||||
setLayout((current) => {
|
||||
const next = normalizeToolbarItemLayout(
|
||||
{ order, placement: current.placement },
|
||||
defaults,
|
||||
);
|
||||
writeLayout(storageKey, next);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[defaults, storageKey],
|
||||
);
|
||||
|
||||
const reorder = useCallback(
|
||||
(draggedId: string, targetId: string, placement: 'before' | 'after' = 'before') => {
|
||||
setLayout((current) => {
|
||||
const next = reorderToolbarItems(current, draggedId, targetId, placement);
|
||||
writeLayout(storageKey, next);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[storageKey],
|
||||
);
|
||||
|
||||
const move = useCallback(
|
||||
(
|
||||
id: string,
|
||||
direction: 'earlier' | 'later',
|
||||
availableIds?: readonly string[] | ReadonlySet<string> | null,
|
||||
) => {
|
||||
setLayout((current) => {
|
||||
const next = moveToolbarItem(current, id, direction, availableIds);
|
||||
writeLayout(storageKey, next);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[storageKey],
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
commit(resetToolbarItemLayout(defaults));
|
||||
}, [commit, defaults]);
|
||||
|
||||
const partition = useCallback(
|
||||
(availableIds?: readonly string[] | ReadonlySet<string>) =>
|
||||
partitionToolbarItems(layout, availableIds),
|
||||
[layout],
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({ layout, partition, setPlacement, setOrder, reorder, move, reset }),
|
||||
[layout, partition, setPlacement, setOrder, reorder, move, reset],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user