mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Add snippet-library search, sort, empty states, storage monitor (M6, §02/§09D)
This commit is contained in:
@@ -25,6 +25,12 @@ import {
|
||||
type Snippet,
|
||||
} from '@core/snippet';
|
||||
import { recomputeDatasetRefs, renameDatasetInSpec } from '@core/spec-refs';
|
||||
import {
|
||||
DEFAULT_SORT_BY,
|
||||
DEFAULT_SORT_ORDER,
|
||||
type SortBy,
|
||||
type SortOrder,
|
||||
} from '@core/snippet-sort';
|
||||
|
||||
/** Which version of the active snippet the editor is showing (spec §03D). */
|
||||
export type EditorView = 'draft' | 'published';
|
||||
@@ -44,6 +50,17 @@ export interface SnippetState {
|
||||
*/
|
||||
bufferEpoch: number;
|
||||
|
||||
/**
|
||||
* Library view state (spec §02 → Search / Sort). These affect only which
|
||||
* snippets are shown and in what order — never `activeSnippetId` or any data.
|
||||
* The component derives its visible list from these primitives via `useMemo`
|
||||
* (core `filterAndSortSnippets`), NOT through a selector that builds a fresh
|
||||
* array (which would loop the app — MEMORY → "Zustand stable selectors").
|
||||
*/
|
||||
searchQuery: string;
|
||||
sortBy: SortBy;
|
||||
sortOrder: SortOrder;
|
||||
|
||||
/** Replace the library from storage and choose an active snippet. */
|
||||
hydrate: (snippets: Snippet[], activeId?: string | null) => void;
|
||||
/**
|
||||
@@ -72,6 +89,16 @@ export interface SnippetState {
|
||||
* an unchanged comment. `now` injectable.
|
||||
*/
|
||||
setComment: (id: string, comment: string, now?: Date) => void;
|
||||
/** Set the live search query (spec §02 → Search). Visibility only — never
|
||||
* touches `activeSnippetId` or any snippet data. */
|
||||
setSearch: (query: string) => void;
|
||||
/**
|
||||
* Set the library sort (spec §02 → Sort): re-selecting the **active** field
|
||||
* flips direction; choosing a **different** field switches to it and resets to
|
||||
* descending. Persisted by an orchestration subscriber; never touches the
|
||||
* active snippet. `order` may be passed to hydrate an exact stored state.
|
||||
*/
|
||||
setSort: (by: SortBy, order?: SortOrder) => void;
|
||||
/**
|
||||
* Duplicate the active snippet (spec §02 → Duplicate): flushes the live buffer
|
||||
* into the source draft first so the copy reflects in-progress edits, then
|
||||
@@ -143,6 +170,9 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
draftText: '',
|
||||
editorView: 'draft',
|
||||
bufferEpoch: 0,
|
||||
searchQuery: '',
|
||||
sortBy: DEFAULT_SORT_BY,
|
||||
sortOrder: DEFAULT_SORT_ORDER,
|
||||
|
||||
hydrate: (snippets, activeId) => {
|
||||
const id = activeId !== undefined ? activeId : newestId(snippets);
|
||||
@@ -243,6 +273,21 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
});
|
||||
},
|
||||
|
||||
setSearch: (searchQuery) => set({ searchQuery }),
|
||||
|
||||
setSort: (by, order) => {
|
||||
set((s) => {
|
||||
// An explicit order hydrates a stored state directly (orchestration init).
|
||||
if (order !== undefined) return { sortBy: by, sortOrder: order };
|
||||
// Re-selecting the active field flips direction; a different field resets
|
||||
// to descending (spec §02 → Sort).
|
||||
if (by === s.sortBy) {
|
||||
return { sortOrder: s.sortOrder === 'desc' ? 'asc' : 'desc' };
|
||||
}
|
||||
return { sortBy: by, sortOrder: 'desc' };
|
||||
});
|
||||
},
|
||||
|
||||
duplicateActiveSnippet: (now, id) => {
|
||||
// Flush the live buffer into the source draft first, so the copy faithfully
|
||||
// mirrors what the user currently sees, not the last auto-saved draft.
|
||||
@@ -396,6 +441,9 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
draftText: '',
|
||||
editorView: 'draft',
|
||||
bufferEpoch: 0,
|
||||
searchQuery: '',
|
||||
sortBy: DEFAULT_SORT_BY,
|
||||
sortOrder: DEFAULT_SORT_ORDER,
|
||||
}),
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user