@@ -159,6 +159,32 @@ const NOUNS = [
159159 'Crumpet' ,
160160]
161161
162+ export function normalizeBlockName ( name : string ) : string {
163+ return name . toLowerCase ( ) . replace ( / \s + / g, '' )
164+ }
165+
166+ export function generateUniqueBlockDuplicateName (
167+ existingNames : string [ ] ,
168+ sourceName : string
169+ ) : string {
170+ const normalizedSet = new Set (
171+ ( existingNames || [ ] ) . filter ( ( n ) => typeof n === 'string' ) . map ( ( n ) => normalizeBlockName ( n ) )
172+ )
173+
174+ const trimmed = ( sourceName || '' ) . trim ( )
175+ const match = trimmed . match ( / ^ ( .* ?) (?: \s + ( \d + ) ) ? $ / )
176+ const baseRaw = match ? match [ 1 ] || '' : trimmed
177+ const base = baseRaw . trim ( ) || 'Block'
178+ const start = match ?. [ 2 ] ? Number . parseInt ( match [ 2 ] , 10 ) + 1 : 1
179+
180+ let n = start
181+ while ( true ) {
182+ const candidate = `${ base } ${ n } `
183+ if ( ! normalizedSet . has ( normalizeBlockName ( candidate ) ) ) return candidate
184+ n += 1
185+ }
186+ }
187+
162188/**
163189 * Generates the next incremental name for entities following pattern: "{prefix} {number}"
164190 *
@@ -170,67 +196,41 @@ export function generateIncrementalName<T extends NameableEntity>(
170196 existingEntities : T [ ] ,
171197 prefix : string
172198) : string {
173- // Create regex pattern for the prefix (e.g., /^Workspace (\d+)$/)
174199 const pattern = new RegExp ( `^${ prefix . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) } (\\d+)$` )
175-
176- // Extract numbers from existing entities that match the pattern
177200 const existingNumbers = existingEntities
178201 . map ( ( entity ) => entity . name . match ( pattern ) )
179202 . filter ( ( match ) => match !== null )
180203 . map ( ( match ) => Number . parseInt ( match ! [ 1 ] , 10 ) )
181-
182- // Find next available number (highest + 1, or 1 if none exist)
183204 const nextNumber = existingNumbers . length > 0 ? Math . max ( ...existingNumbers ) + 1 : 1
184-
185205 return `${ prefix } ${ nextNumber } `
186206}
187207
188- /**
189- * Generates the next workspace name
190- */
191208export async function generateWorkspaceName ( ) : Promise < string > {
192209 const response = await fetch ( '/api/workspaces' )
193210 const data = ( await response . json ( ) ) as WorkspacesApiResponse
194211 const workspaces = data . workspaces || [ ]
195-
196212 return generateIncrementalName ( workspaces , 'Workspace' )
197213}
198214
199- /**
200- * Generates the next folder name for a workspace
201- */
202215export async function generateFolderName ( workspaceId : string ) : Promise < string > {
203216 const response = await fetch ( `/api/folders?workspaceId=${ workspaceId } ` )
204217 const data = ( await response . json ( ) ) as FoldersApiResponse
205218 const folders = data . folders || [ ]
206-
207- // Filter to only root-level folders (parentId is null)
208219 const rootFolders = folders . filter ( ( folder ) => folder . parentId === null )
209-
210220 return generateIncrementalName ( rootFolders , 'Folder' )
211221}
212222
213- /**
214- * Generates the next subfolder name for a parent folder
215- */
216223export async function generateSubfolderName (
217224 workspaceId : string ,
218225 parentFolderId : string
219226) : Promise < string > {
220227 const response = await fetch ( `/api/folders?workspaceId=${ workspaceId } ` )
221228 const data = ( await response . json ( ) ) as FoldersApiResponse
222229 const folders = data . folders || [ ]
223-
224- // Filter to only subfolders of the specified parent
225230 const subfolders = folders . filter ( ( folder ) => folder . parentId === parentFolderId )
226-
227231 return generateIncrementalName ( subfolders , 'Subfolder' )
228232}
229233
230- /**
231- * Generates a creative workflow name using random adjectives and nouns
232- * @returns A creative workflow name like "blazing-phoenix" or "crystal-dragon"
233- */
234234export function generateCreativeWorkflowName ( ) : string {
235235 const adjective = ADJECTIVES [ Math . floor ( Math . random ( ) * ADJECTIVES . length ) ]
236236 const noun = NOUNS [ Math . floor ( Math . random ( ) * NOUNS . length ) ]
0 commit comments