1111 * - Canvas config determines layout
1212 */
1313
14- import { useCallback , type ReactNode } from 'react'
14+ import React , { useCallback , useState , type ReactNode } from 'react'
1515import type { Column } from '../../types/app-types'
1616import type {
1717 PullRequest ,
@@ -167,6 +167,7 @@ export function CanvasRenderer({
167167 activeCanvas,
168168 resizeColumn,
169169 reorderColumns,
170+ setColumnPanel,
170171 } = useCanvas ( )
171172
172173 // Render a list panel based on column config
@@ -293,6 +294,8 @@ export function CanvasRenderer({
293294 onDoubleClickStash = { handlers . onDoubleClickStash }
294295 onSelectRepo = { handlers . onSelectRepo }
295296 onDoubleClickRepo = { handlers . onDoubleClickRepo }
297+ onCreateBranch = { handlers . onCreateBranch }
298+ onCreateWorktree = { handlers . onCreateWorktree }
296299 formatRelativeTime = { handlers . formatRelativeTime }
297300 />
298301 )
@@ -322,18 +325,75 @@ export function CanvasRenderer({
322325 // Render a viz panel based on column config
323326 const renderVizSlot = useCallback (
324327 ( column : Column ) : ReactNode => {
328+ // Shared viz header with chart selector
329+ const VizHeader = ( {
330+ panel,
331+ label,
332+ icon
333+ } : {
334+ panel : string
335+ label : string
336+ icon : string
337+ } ) => {
338+ const [ controlsOpen , setControlsOpen ] = useState ( false )
339+
340+ const chartOptions = [
341+ { id : 'git-graph' , label : 'Git Graph' , icon : '◉' } ,
342+ { id : 'timeline' , label : 'Timeline' , icon : '◔' } ,
343+ { id : 'tech-tree' , label : 'Tech Tree' , icon : '⬡' } ,
344+ ]
345+
346+ return (
347+ < >
348+ < div
349+ className = { `column-header clickable-header ${ controlsOpen ? 'open' : '' } ` }
350+ onClick = { ( ) => setControlsOpen ( ! controlsOpen ) }
351+ >
352+ < div className = "column-title" >
353+ < h2 >
354+ < span className = "column-icon" > { icon } </ span >
355+ { label }
356+ </ h2 >
357+ < span className = { `header-chevron ${ controlsOpen ? 'open' : '' } ` } > ▾</ span >
358+ </ div >
359+ </ div >
360+ { controlsOpen && (
361+ < div className = "column-controls" onClick = { ( e ) => e . stopPropagation ( ) } >
362+ < div className = "control-row" >
363+ < label > Chart</ label >
364+ < select
365+ value = { panel }
366+ onChange = { ( e ) => {
367+ // Update column panel through canvas context
368+ if ( activeCanvas ) {
369+ setColumnPanel ( activeCanvas . id , column . id , e . target . value as import ( '../../types/app-types' ) . PanelType )
370+ }
371+ setControlsOpen ( false )
372+ } }
373+ className = "control-select"
374+ >
375+ { chartOptions . map ( ( opt ) => (
376+ < option key = { opt . id } value = { opt . id } >
377+ { opt . icon } { opt . label }
378+ </ option >
379+ ) ) }
380+ </ select >
381+ </ div >
382+ </ div >
383+ ) }
384+ </ >
385+ )
386+ }
387+
325388 switch ( column . panel ) {
326389 case 'git-graph' :
327390 return (
328391 < div className = "viz-panel git-graph-panel" >
329- < div className = "column-header" >
330- < div className = "column-title" >
331- < h2 >
332- < span className = "column-icon" > { column . icon || '◉' } </ span >
333- { column . label || 'History' }
334- </ h2 >
335- </ div >
336- </ div >
392+ < VizHeader
393+ panel = { column . panel }
394+ label = { column . label || 'History' }
395+ icon = { column . icon || '◉' }
396+ />
337397 < div className = "viz-panel-content" >
338398 < GitGraph
339399 commits = { data . commits }
@@ -349,27 +409,31 @@ export function CanvasRenderer({
349409 case 'timeline' :
350410 return (
351411 < div className = "viz-panel timeline-panel" >
352- < ContributorChart
353- topN = { 10 }
354- bucketSize = "week"
355- height = { 500 }
356- invertedTheme = { true }
357- onManageUsers = { handlers . onOpenMailmap }
412+ < VizHeader
413+ panel = { column . panel }
414+ label = { column . label || 'Timeline' }
415+ icon = { column . icon || '◔' }
358416 />
417+ < div className = "viz-panel-content" >
418+ < ContributorChart
419+ topN = { 10 }
420+ bucketSize = "week"
421+ height = { 500 }
422+ invertedTheme = { true }
423+ onManageUsers = { handlers . onOpenMailmap }
424+ />
425+ </ div >
359426 </ div >
360427 )
361428
362429 case 'tech-tree' :
363430 return (
364431 < div className = "viz-panel tech-tree-panel" >
365- < div className = "column-header" >
366- < div className = "column-title" >
367- < h2 >
368- < span className = "column-icon" > { column . icon || '⬡' } </ span >
369- { column . label || 'Tech Tree' }
370- </ h2 >
371- </ div >
372- </ div >
432+ < VizHeader
433+ panel = { column . panel }
434+ label = { column . label || 'Tech Tree' }
435+ icon = { column . icon || '⬡' }
436+ />
373437 < div className = "viz-panel-content" >
374438 < TechTreeChart
375439 limit = { 25 }
@@ -390,7 +454,7 @@ export function CanvasRenderer({
390454 )
391455 }
392456 } ,
393- [ data . commits , selection . selectedCommit , handlers ]
457+ [ data . commits , selection . selectedCommit , handlers , activeCanvas , setColumnPanel ]
394458 )
395459
396460 // Render an editor panel based on column config
0 commit comments