From 3336ed6790070bb20f2a1b2e8a2ae514ee818458 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 13 Jan 2026 20:44:36 +1300 Subject: [PATCH] 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 --- src/store/utils/localStorage.ts | 25 ++++++++++++- src/store/workflowStore.ts | 32 ++++++++++++++++ src/types/index.ts | 66 ++++----------------------------- 3 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/store/utils/localStorage.ts b/src/store/utils/localStorage.ts index b7712fc8..ec7b1c40 100644 --- a/src/store/utils/localStorage.ts +++ b/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)}`; diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 8e141528..329f4a02 100644 --- a/src/store/workflowStore.ts +++ b/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((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((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 }); + }, })); diff --git a/src/types/index.ts b/src/types/index.ts index c8f8dcc0..42f91f61 100644 --- a/src/types/index.ts +++ b/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 { - hasPause?: boolean; -} - -// Workflow Edge -export type WorkflowEdge = Edge; - // 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; -} -