Browse Source

feat(23-01): add recently used models tracking and storage

- Add RecentModel interface to types/index.ts
- Add localStorage helpers: getRecentModels, saveRecentModels, MAX_RECENT_MODELS
- Add recentModels state to workflowStore (initialized from localStorage)
- Add trackModelUsage action to track model usage with timestamps
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
3336ed6790
  1. 25
      src/store/utils/localStorage.ts
  2. 32
      src/store/workflowStore.ts
  3. 66
      src/types/index.ts

25
src/store/utils/localStorage.ts

@ -1,10 +1,14 @@
import { WorkflowSaveConfig, WorkflowCostData, ProviderSettings } from "@/types";
import { WorkflowSaveConfig, WorkflowCostData, ProviderSettings, RecentModel } from "@/types";
// Storage keys
export const STORAGE_KEY = "node-banana-workflow-configs";
export const COST_DATA_STORAGE_KEY = "node-banana-workflow-costs";
export const GENERATE_IMAGE_DEFAULTS_KEY = "node-banana-nanoBanana-defaults";
export const PROVIDER_SETTINGS_KEY = "node-banana-provider-settings";
export const RECENT_MODELS_KEY = "node-banana-recent-models";
// Maximum recent models to store (show 4 in UI, keep 8 for persistence)
export const MAX_RECENT_MODELS = 8;
// GenerateImage defaults interface
export interface GenerateImageDefaults {
@ -122,6 +126,25 @@ export const saveProviderSettings = (settings: ProviderSettings): void => {
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(settings));
};
// Recent models helpers
export const getRecentModels = (): RecentModel[] => {
if (typeof window === "undefined") return [];
const stored = localStorage.getItem(RECENT_MODELS_KEY);
if (stored) {
try {
return JSON.parse(stored) as RecentModel[];
} catch {
return [];
}
}
return [];
};
export const saveRecentModels = (models: RecentModel[]): void => {
if (typeof window === "undefined") return;
localStorage.setItem(RECENT_MODELS_KEY, JSON.stringify(models));
};
// Workflow ID generator
export const generateWorkflowId = (): string =>
`wf_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

32
src/store/workflowStore.ts

@ -25,6 +25,7 @@ import {
GroupColor,
ProviderType,
ProviderSettings,
RecentModel,
} from "@/types";
import { useToast } from "@/components/Toast";
import { calculateGenerationCost } from "@/utils/costCalculator";
@ -38,6 +39,9 @@ import {
getProviderSettings,
saveProviderSettings,
defaultProviderSettings,
getRecentModels,
saveRecentModels,
MAX_RECENT_MODELS,
} from "./utils/localStorage";
import {
createDefaultNodeData,
@ -179,6 +183,12 @@ interface WorkflowStore {
// Model search dialog actions
setModelSearchOpen: (open: boolean, provider?: ProviderType | null) => void;
// Recent models state
recentModels: RecentModel[];
// Recent models actions
trackModelUsage: (model: { provider: ProviderType; modelId: string; displayName: string }) => void;
}
let nodeIdCounter = 0;
@ -224,6 +234,9 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
modelSearchOpen: false,
modelSearchProvider: null,
// Recent models initial state
recentModels: getRecentModels(),
setEdgeStyle: (style: EdgeStyle) => {
set({ edgeStyle: style });
},
@ -2544,4 +2557,23 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
modelSearchProvider: provider ?? null,
});
},
trackModelUsage: (model: { provider: ProviderType; modelId: string; displayName: string }) => {
const current = get().recentModels;
// Remove existing entry for same modelId if present
const filtered = current.filter((m) => m.modelId !== model.modelId);
// Prepend new entry with current timestamp
const updated: RecentModel[] = [
{
provider: model.provider,
modelId: model.modelId,
displayName: model.displayName,
timestamp: Date.now(),
},
...filtered,
].slice(0, MAX_RECENT_MODELS);
// Save to localStorage and update state
saveRecentModels(updated);
set({ recentModels: updated });
},
}));

66
src/types/index.ts

@ -1,9 +1,13 @@
import { Edge } from "@xyflow/react";
// Re-export all node and annotation types from domain files
// Re-export all types from domain files
export * from "./annotation";
export * from "./nodes";
export * from "./providers";
export * from "./models";
export * from "./workflow";
// Import types for use in this file
import type { ProviderType, LLMProvider, LLMModelType } from "./providers";
import type { AspectRatio, Resolution, ModelType } from "./models";
// Recently used models tracking
export interface RecentModel {
@ -13,26 +17,6 @@ export interface RecentModel {
timestamp: number;
}
// Import provider types for use in this file
import type { ProviderType, LLMProvider, LLMModelType } from "./providers";
// Aspect Ratios (supported by both Nano Banana and Nano Banana Pro)
export type AspectRatio = "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9";
// Resolution Options (only supported by Nano Banana Pro)
export type Resolution = "1K" | "2K" | "4K";
// Image Generation Model Options
export type ModelType = "nano-banana" | "nano-banana-pro";
// Workflow Edge Data
export interface WorkflowEdgeData extends Record<string, unknown> {
hasPause?: boolean;
}
// Workflow Edge
export type WorkflowEdge = Edge<WorkflowEdgeData>;
// API Request/Response types for Image Generation
export interface GenerateRequest {
images: string[]; // Now supports multiple images
@ -67,39 +51,3 @@ export interface LLMGenerateResponse {
text?: string;
error?: string;
}
// Auto-save configuration stored in localStorage
export interface WorkflowSaveConfig {
workflowId: string;
name: string;
directoryPath: string;
generationsPath: string | null;
lastSavedAt: number | null;
}
// Cost tracking data stored per-workflow in localStorage
export interface WorkflowCostData {
workflowId: string;
incurredCost: number;
lastUpdated: number;
}
// Group background color options (dark mode tints)
export type GroupColor =
| "neutral"
| "blue"
| "green"
| "purple"
| "orange"
| "red";
// Group definition stored in workflow
export interface NodeGroup {
id: string;
name: string;
color: GroupColor;
position: { x: number; y: number };
size: { width: number; height: number };
locked?: boolean;
}

Loading…
Cancel
Save