← back to jeffnv__lockin

Function bodies 52 total

All specs Real LLM only Function bodies
startBlocker function · go · L11-L29 (19 LOC)
blocker.go
func startBlocker(apps []string, paused *atomic.Bool, stop chan struct{}) tea.Cmd {
	return func() tea.Msg {
		killApps(apps)

		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-stop:
				return blockerStoppedMsg{}
			case <-ticker.C:
				if !paused.Load() {
					killApps(apps)
				}
			}
		}
	}
}
killApps function · go · L31-L35 (5 LOC)
blocker.go
func killApps(apps []string) {
	for _, app := range apps {
		_ = exec.Command("pkill", "-x", app).Run()
	}
}
timerColor method · go · L28-L38 (11 LOC)
colors.go
func (m model) timerColor() lipgloss.Color {
	frac := float64(m.remaining) / float64(m.totalDuration)
	switch {
	case frac <= 0.10:
		return colorRed
	case frac <= 0.25:
		return colorYellow
	default:
		return colorGreen
	}
}
init function · go · L19-L48 (30 LOC)
fonts.go
func init() {
	for _, f := range fonts {
		// Find max rune width across digit glyphs (skip ':')
		maxWidth := 0
		for ch, rows := range f.digits {
			if ch == ':' {
				continue
			}
			for _, row := range rows {
				if w := utf8.RuneCountInString(row); w > maxWidth {
					maxWidth = w
				}
			}
		}
		// Center-pad each digit row to maxWidth
		for ch, rows := range f.digits {
			if ch == ':' {
				continue
			}
			for i, row := range rows {
				w := utf8.RuneCountInString(row)
				if w < maxWidth {
					left := (maxWidth - w) / 2
					right := maxWidth - w - left
					rows[i] = strings.Repeat(" ", left) + row + strings.Repeat(" ", right)
				}
			}
		}
	}
}
renderDigit function · go · L50-L56 (7 LOC)
fonts.go
func renderDigit(f *fontData, ch rune) string {
	rows, ok := f.digits[ch]
	if !ok {
		return string(ch)
	}
	return strings.Join(rows, "\n")
}
fontSpacer function · go · L58-L64 (7 LOC)
fonts.go
func fontSpacer(height int) string {
	rows := make([]string, height)
	for i := range rows {
		rows[i] = " "
	}
	return strings.Join(rows, "\n")
}
parseArgs function · go · L22-L99 (78 LOC)
main.go
func parseArgs(args []string) config {
	if len(args) == 0 {
		printUsage()
		os.Exit(1)
	}

	var cfg config
	var positional []string

	for i := 0; i < len(args); i++ {
		switch args[i] {
		case "-h", "--help":
			printUsage()
			os.Exit(0)
		case "--block":
			if i+1 >= len(args) {
				fmt.Fprintln(os.Stderr, "error: --block requires an argument")
				os.Exit(1)
			}
			i++
			cfg.blockApps = strings.Split(args[i], ",")
		case "--viz":
			if i+1 >= len(args) {
				fmt.Fprintln(os.Stderr, "error: --viz requires an argument")
				os.Exit(1)
			}
			i++
			switch args[i] {
			case "bar", "defrag", "binary", "bubble", "merge", "quick":
				cfg.vizMode = args[i]
			default:
				fmt.Fprintf(os.Stderr, "error: unknown viz mode %q (use bar, defrag, binary, bubble, merge, or quick)\n", args[i])
				os.Exit(1)
			}
		case "--font":
			if i+1 >= len(args) {
				fmt.Fprintln(os.Stderr, "error: --font requires an argument")
				os.Exit(1)
			}
			i++
			switch args[i] {
			case "block", "slim", "do
About: code-quality intelligence by Repobility · https://repobility.com
printUsage function · go · L101-L117 (17 LOC)
main.go
func printUsage() {
	fmt.Fprintln(os.Stderr, `Usage: lockin <duration> [task name] [flags]

Duration formats: 30s, 5m, 30m, 1h, 1h30m

Flags:
  --block App1,App2        Block apps while timer runs
  --viz bar|defrag|binary|bubble|merge|quick
                           Visualization mode
  --font block|slim|dot    Timer font style

Examples:
  lockin 30m "deep work"
  lockin 25m --block Safari,Messages,Discord
  lockin 1h30m --viz defrag
  lockin 25m --font slim --viz binary`)
}
listenSIGUSR1 function · go · L119-L125 (7 LOC)
main.go
func listenSIGUSR1(p *tea.Program) {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGUSR1)
	for range sig {
		p.Send(togglePauseMsg{})
	}
}
main function · go · L127-L147 (21 LOC)
main.go
func main() {
	cfg := parseArgs(os.Args[1:])
	m := newModel(cfg)
	p := tea.NewProgram(m, tea.WithAltScreen())

	go listenSIGUSR1(p)

	finalModel, err := p.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	if fm, ok := finalModel.(model); ok && fm.remaining <= 0 {
		fmt.Printf("lockin: %s complete", cfg.duration)
		if cfg.taskName != "" {
			fmt.Printf(" — %s", cfg.taskName)
		}
		fmt.Println()
	}
}
newModel function · go · L51-L66 (16 LOC)
model.go
func newModel(cfg config) model {
	m := model{
		totalDuration: cfg.duration,
		remaining:     cfg.duration,
		taskName:      cfg.taskName,
		blockApps:     cfg.blockApps,
		vizMode:       cfg.vizMode,
		font:          fonts[cfg.fontStyle],
		blockerStop:   make(chan struct{}),
		blockerPaused: &atomic.Bool{},
	}
	if m.isDotFont() {
		m.updateDotFade()
	}
	return m
}
doTick function · go · L71-L75 (5 LOC)
model.go
func doTick() tea.Cmd {
	return tea.Tick(time.Second, func(t time.Time) tea.Msg {
		return tickMsg(t)
	})
}
doVizTick function · go · L77-L81 (5 LOC)
model.go
func doVizTick() tea.Cmd {
	return tea.Tick(100*time.Millisecond, func(t time.Time) tea.Msg {
		return vizTickMsg{}
	})
}
needsFastTick method · go · L83-L92 (10 LOC)
model.go
func (m model) needsFastTick() bool {
	if m.isDotFont() {
		return true
	}
	switch m.vizMode {
	case "bar", "binary", "bubble", "merge", "quick":
		return true
	}
	return false
}
Init method · go · L94-L103 (10 LOC)
model.go
func (m model) Init() tea.Cmd {
	cmds := []tea.Cmd{doTick()}
	if len(m.blockApps) > 0 {
		cmds = append(cmds, startBlocker(m.blockApps, m.blockerPaused, m.blockerStop))
	}
	if m.needsFastTick() {
		cmds = append(cmds, doVizTick())
	}
	return tea.Batch(cmds...)
}
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
Update method · go · L105-L183 (79 LOC)
model.go
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {

	case tea.WindowSizeMsg:
		m.width = msg.Width
		m.height = msg.Height
		if m.vizMode == "defrag" {
			m.initDefragGrid()
		}
		if m.vizMode == "bubble" || m.vizMode == "merge" || m.vizMode == "quick" {
			m.initSortGrid()
		}
		return m, nil

	case tea.KeyMsg:
		switch msg.String() {
		case "ctrl+c", "q":
			m.done = true
			m.shutdown()
			return m, tea.Quit
		case " ":
			m.paused = !m.paused
			m.blockerPaused.Store(m.paused)
			if !m.paused && m.needsFastTick() {
				m.lastTickAt = time.Now()
				return m, doVizTick()
			}
			return m, nil
		}
		return m, nil

	case togglePauseMsg:
		m.paused = !m.paused
		m.blockerPaused.Store(m.paused)
		if !m.paused && m.needsFastTick() {
			m.lastTickAt = time.Now()
			return m, doVizTick()
		}
		return m, nil

	case vizTickMsg:
		if m.paused || m.done {
			return m, nil
		}
		return m, doVizTick()

	case tickMsg:
		if m.paused || m.done {
			return m, doTic
shutdown method · go · L185-L191 (7 LOC)
model.go
func (m *model) shutdown() {
	select {
	case <-m.blockerStop:
	default:
		close(m.blockerStop)
	}
}
View method · go · L193-L238 (46 LOC)
model.go
func (m model) View() string {
	if m.done {
		return ""
	}

	var sections []string

	// Task name
	if m.taskName != "" {
		style := lipgloss.NewStyle().
			Bold(true).
			Foreground(lipgloss.Color("7"))
		sections = append(sections, style.Render(m.taskName))
	}

	// Spacer
	sections = append(sections, "")

	// Big timer digits
	sections = append(sections, m.renderBigTimer())

	// Pause indicator
	if m.paused {
		style := lipgloss.NewStyle().
			Bold(true).
			Foreground(lipgloss.Color("11"))
		sections = append(sections, "")
		sections = append(sections, style.Render("PAUSED"))
	}

	// Visualization
	if m.vizMode != "" {
		sections = append(sections, "")
		sections = append(sections, m.renderViz())
	}

	// Blocked apps
	if len(m.blockApps) > 0 {
		sections = append(sections, "")
		sections = append(sections, m.renderBlockedApps())
	}

	body := lipgloss.JoinVertical(lipgloss.Center, sections...)

	return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, body)
}
timerTimeStr method · go · L245-L253 (9 LOC)
model.go
func (m model) timerTimeStr() string {
	h := int(m.remaining.Hours())
	min := int(m.remaining.Minutes()) % 60
	sec := int(m.remaining.Seconds()) % 60
	if h > 0 {
		return fmt.Sprintf("%d:%02d:%02d", h, min, sec)
	}
	return fmt.Sprintf("%02d:%02d", min, sec)
}
timerCols method · go · L259-L278 (20 LOC)
model.go
func (m model) timerCols() []glyphCol {
	timeStr := m.timerTimeStr()
	var cols []glyphCol
	for i, ch := range timeStr {
		if rows, ok := m.font.digits[ch]; ok {
			cols = append(cols, glyphCol{rows})
		}
		if i < len(timeStr)-1 {
			nextCh := rune(timeStr[i+1])
			if ch != ':' && nextCh != ':' {
				spacerRows := make([]string, m.font.height)
				for j := range spacerRows {
					spacerRows[j] = "  "
				}
				cols = append(cols, glyphCol{spacerRows})
			}
		}
	}
	return cols
}
buildTimerGrid method · go · L280-L291 (12 LOC)
model.go
func (m model) buildTimerGrid() [][]rune {
	cols := m.timerCols()
	var grid [][]rune
	for row := 0; row < m.font.height; row++ {
		var rowRunes []rune
		for _, c := range cols {
			rowRunes = append(rowRunes, []rune(c.rows[row])...)
		}
		grid = append(grid, rowRunes)
	}
	return grid
}
updateDotFade method · go · L293-L334 (42 LOC)
model.go
func (m *model) updateDotFade() {
	grid := m.buildTimerGrid()
	if len(grid) == 0 {
		return
	}

	width := len(grid[0])
	total := m.font.height * width

	currentCells := make([]bool, total)
	for r, row := range grid {
		for c, ch := range row {
			if ch != ' ' {
				currentCells[r*width+c] = true
			}
		}
	}

	if len(m.dotPrevCells) != total {
		m.dotPrevCells = currentCells
		m.dotOnAt = make([]time.Time, total)
		m.dotOffAt = make([]time.Time, total)
		now := time.Now()
		for i, on := range currentCells {
			if on {
				m.dotOnAt[i] = now
			}
		}
		return
	}

	now := time.Now()
	for i := range currentCells {
		if !m.dotPrevCells[i] && currentCells[i] {
			m.dotOnAt[i] = now
		}
		if m.dotPrevCells[i] && !currentCells[i] {
			m.dotOffAt[i] = now
		}
	}
	m.dotPrevCells = currentCells
}
renderBigTimer method · go · L336-L452 (117 LOC)
model.go
func (m model) renderBigTimer() string {
	baseColor := m.timerColor()
	cols := m.timerCols()

	isBlock := m.isBlockFont()
	isDot := m.isDotFont()

	if !isBlock && !isDot {
		// Default path: per-row gradient (slim font, etc.)
		var renderedRows []string
		for row := 0; row < m.font.height; row++ {
			color := shaderGradient(row, 0, m.font.height, len(cols), baseColor, 0)
			style := lipgloss.NewStyle().Foreground(color)
			var rowStr strings.Builder
			for _, c := range cols {
				rowStr.WriteString(style.Render(c.rows[row]))
			}
			renderedRows = append(renderedRows, rowStr.String())
		}
		return strings.Join(renderedRows, "\n")
	}

	// Build 2D rune grid for per-cell rendering
	var grid [][]rune
	for row := 0; row < m.font.height; row++ {
		var rowRunes []rune
		for _, c := range cols {
			rowRunes = append(rowRunes, []rune(c.rows[row])...)
		}
		grid = append(grid, rowRunes)
	}
	if len(grid) == 0 {
		return ""
	}
	gridWidth := len(grid[0])

	renderHeight := m.font.height
	if isBloc
Open data scored by Repobility · https://repobility.com
renderBlockedApps method · go · L454-L462 (9 LOC)
model.go
func (m model) renderBlockedApps() string {
	var parts []string
	for _, app := range m.blockApps {
		parts = append(parts, "🔒 "+app)
	}
	style := lipgloss.NewStyle().
		Foreground(colorDim)
	return style.Render(strings.Join(parts, "  "))
}
clamp01 function · go · L18-L26 (9 LOC)
shaders.go
func clamp01(v float64) float64 {
	if v < 0 {
		return 0
	}
	if v > 1 {
		return 1
	}
	return v
}
hexToRGB function · go · L28-L35 (8 LOC)
shaders.go
func hexToRGB(hex string) (uint8, uint8, uint8) {
	var r, g, b uint8
	if len(hex) > 0 && hex[0] == '#' {
		hex = hex[1:]
	}
	fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b)
	return r, g, b
}
rgbToHex function · go · L37-L39 (3 LOC)
shaders.go
func rgbToHex(r, g, b uint8) string {
	return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
rgbToHSL function · go · L41-L78 (38 LOC)
shaders.go
func rgbToHSL(r, g, b uint8) hsl {
	rf := float64(r) / 255.0
	gf := float64(g) / 255.0
	bf := float64(b) / 255.0

	max := math.Max(rf, math.Max(gf, bf))
	min := math.Min(rf, math.Min(gf, bf))

	l := (max + min) / 2.0

	if max == min {
		return hsl{0, 0, l}
	}

	d := max - min
	var s float64
	if l > 0.5 {
		s = d / (2.0 - max - min)
	} else {
		s = d / (max + min)
	}

	var h float64
	switch max {
	case rf:
		h = (gf - bf) / d
		if gf < bf {
			h += 6
		}
	case gf:
		h = (bf-rf)/d + 2
	case bf:
		h = (rf-gf)/d + 4
	}
	h /= 6

	return hsl{h, s, l}
}
hueToRGB function · go · L80-L97 (18 LOC)
shaders.go
func hueToRGB(p, q, t float64) float64 {
	if t < 0 {
		t += 1
	}
	if t > 1 {
		t -= 1
	}
	if t < 1.0/6.0 {
		return p + (q-p)*6*t
	}
	if t < 1.0/2.0 {
		return q
	}
	if t < 2.0/3.0 {
		return p + (q-p)*(2.0/3.0-t)*6
	}
	return p
}
hslToRGB function · go · L99-L118 (20 LOC)
shaders.go
func hslToRGB(c hsl) (uint8, uint8, uint8) {
	if c.s == 0 {
		v := uint8(clamp01(c.l) * 255)
		return v, v, v
	}

	var q float64
	if c.l < 0.5 {
		q = c.l * (1 + c.s)
	} else {
		q = c.l + c.s - c.l*c.s
	}
	p := 2*c.l - q

	r := uint8(clamp01(hueToRGB(p, q, c.h+1.0/3.0)) * 255)
	g := uint8(clamp01(hueToRGB(p, q, c.h)) * 255)
	b := uint8(clamp01(hueToRGB(p, q, c.h-1.0/3.0)) * 255)

	return r, g, b
}
modifyColor function · go · L120-L134 (15 LOC)
shaders.go
func modifyColor(base lipgloss.Color, fn func(hsl) hsl) lipgloss.Color {
	hex := string(base)
	if len(hex) > 0 && hex[0] != '#' {
		if h, ok := ansiToHex[hex]; ok {
			hex = h
		}
	}
	r, g, b := hexToRGB(hex)
	c := rgbToHSL(r, g, b)
	c = fn(c)
	c.l = clamp01(c.l)
	c.s = clamp01(c.s)
	nr, ng, nb := hslToRGB(c)
	return lipgloss.Color(rgbToHex(nr, ng, nb))
}
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
shaderGradient function · go · L137-L146 (10 LOC)
shaders.go
func shaderGradient(row, col, height, cols int, base lipgloss.Color, t float64) lipgloss.Color {
	if height <= 1 {
		return base
	}
	frac := float64(row) / float64(height-1) // 0 at top, 1 at bottom
	return modifyColor(base, func(c hsl) hsl {
		c.l = clamp01(c.l * (1.15 - frac*0.65))
		return c
	})
}
progressFraction method · go · L13-L28 (16 LOC)
viz.go
func (m model) progressFraction() float64 {
	if m.totalDuration == 0 {
		return 0
	}
	elapsed := m.totalDuration - m.remaining
	// Interpolate sub-second progress using wall clock
	if !m.paused && !m.done && !m.lastTickAt.IsZero() {
		elapsed += time.Since(m.lastTickAt)
	}
	// Finish viz with 10% of time remaining so the completed state is visible
	frac := float64(elapsed) / (float64(m.totalDuration) * 0.9)
	if frac > 1 {
		frac = 1
	}
	return frac
}
neonPulse function · go · L31-L37 (7 LOC)
viz.go
func neonPulse(width float64) (pos, pulseWidth float64) {
	const period = 2.0 // seconds per sweep
	t := math.Mod(float64(time.Now().UnixMilli())/1000.0, period) / period
	pos = t * width
	pulseWidth = math.Max(width*0.05, 1.0)
	return
}
pulseBoost function · go · L39-L42 (4 LOC)
viz.go
func pulseBoost(x, pulsePos, pulseWidth float64) float64 {
	dist := math.Abs(x - pulsePos)
	return math.Exp(-(dist * dist) / (2 * pulseWidth * pulseWidth))
}
renderViz method · go · L44-L57 (14 LOC)
viz.go
func (m model) renderViz() string {
	switch m.vizMode {
	case "bar":
		return m.renderBar()
	case "defrag":
		return m.renderDefrag()
	case "binary":
		return m.renderBinary()
	case "bubble", "merge", "quick":
		return m.renderSort()
	default:
		return ""
	}
}
renderBar method · go · L61-L102 (42 LOC)
viz.go
func (m model) renderBar() string {
	maxWidth := 60
	if m.width-4 < maxWidth {
		maxWidth = m.width - 4
	}
	if maxWidth < 10 {
		maxWidth = 10
	}

	frac := m.progressFraction()
	filled := int(frac * float64(maxWidth))
	if filled > maxWidth {
		filled = maxWidth
	}

	baseColor := m.timerColor()
	pulsePos, pulseW := neonPulse(float64(maxWidth))

	var bar strings.Builder
	for i := 0; i < maxWidth; i++ {
		boost := pulseBoost(float64(i), pulsePos, pulseW)
		var base lipgloss.Color
		var ch string
		if i < filled {
			base = baseColor
			ch = "█"
		} else {
			base = colorDim
			ch = "░"
		}
		color := modifyColor(base, func(c hsl) hsl {
			c.l = clamp01(c.l + boost*0.35)
			return c
		})
		bar.WriteString(lipgloss.NewStyle().Foreground(color).Render(ch))
	}

	pct := fmt.Sprintf(" %d%%", int(frac*100))
	pctStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("7"))

	return bar.String() + pctStyle.Render(pct)
}
initDefragGrid method · go · L106-L127 (22 LOC)
viz.go
func (m *model) initDefragGrid() {
	w := m.defragGridWidth()
	h := 8
	total := w * h

	if m.defragWidth == w && len(m.defragOriginal) == total {
		return
	}

	m.defragWidth = w
	m.defragOriginal = make([]uint8, total)

	// Fill ~65% of cells with data, rest free
	dataCount := total * 65 / 100
	for i := 0; i < dataCount; i++ {
		m.defragOriginal[i] = 1
	}
	// Shuffle to create chaotic layout
	rand.Shuffle(total, func(i, j int) {
		m.defragOriginal[i], m.defragOriginal[j] = m.defragOriginal[j], m.defragOriginal[i]
	})
}
defragGridWidth method · go · L129-L137 (9 LOC)
viz.go
func (m model) defragGridWidth() int {
	w := m.width * 6 / 10
	if w < 10 {
		w = 10
	}
	// Each cell renders as 2 chars (██ or ░░), so halve the available width
	w = w / 2
	return w
}
About: code-quality intelligence by Repobility · https://repobility.com
renderDefrag method · go · L139-L187 (49 LOC)
viz.go
func (m model) renderDefrag() string {
	if len(m.defragOriginal) == 0 || m.defragWidth == 0 {
		return ""
	}

	total := len(m.defragOriginal)
	frac := m.progressFraction()
	cursor := int(frac * float64(total))
	if cursor > total {
		cursor = total
	}

	// Count data cells in the processed region (0..cursor)
	dataInProcessed := 0
	for i := 0; i < cursor; i++ {
		if m.defragOriginal[i] == 1 {
			dataInProcessed++
		}
	}

	dataStyle := lipgloss.NewStyle().Foreground(colorDefragData)
	fragStyle := lipgloss.NewStyle().Foreground(colorDefragFrag)
	freeStyle := lipgloss.NewStyle().Foreground(colorDefragFree)

	var rows []string
	for rowStart := 0; rowStart < total; rowStart += m.defragWidth {
		end := rowStart + m.defragWidth
		if end > total {
			end = total
		}
		var row strings.Builder
		for i := rowStart; i < end; i++ {
			if i < dataInProcessed {
				row.WriteString(dataStyle.Render("██"))
			} else if i < cursor {
				row.WriteString(freeStyle.Render("░░"))
			} else {
				if m.defragOr
rainbowColor function · go · L191-L193 (3 LOC)
viz.go
func rainbowColor(value, total int) lipgloss.Color {
	return rainbowColorL(value, total, 0.5)
}
rainbowColorL function · go · L195-L199 (5 LOC)
viz.go
func rainbowColorL(value, total int, lightness float64) lipgloss.Color {
	h := float64(value) / float64(total) * 0.85 // stop before wrapping back to red
	r, g, b := hslToRGB(hsl{h, 1.0, lightness})
	return lipgloss.Color(rgbToHex(r, g, b))
}
initSortGrid method · go · L201-L241 (41 LOC)
viz.go
func (m *model) initSortGrid() {
	w := m.defragGridWidth()
	h := 4
	total := w * h

	if m.sortWidth == w && len(m.sortFrames) > 0 {
		return
	}

	m.sortWidth = w

	arr := make([]int, total)
	for i := range arr {
		arr[i] = i
	}
	rand.Shuffle(total, func(i, j int) {
		arr[i], arr[j] = arr[j], arr[i]
	})

	var frames [][]int
	switch m.vizMode {
	case "bubble":
		frames = bubbleSortFrames(arr)
	case "merge":
		frames = mergeSortFrames(arr)
	case "quick":
		frames = quickSortFrames(arr)
	}

	// Subsample to cap memory usage
	const maxFrames = 2000
	if len(frames) <= maxFrames {
		m.sortFrames = frames
		return
	}
	subsampled := make([][]int, maxFrames)
	for i := range subsampled {
		subsampled[i] = frames[i*(len(frames)-1)/(maxFrames-1)]
	}
	m.sortFrames = subsampled
}
renderSort method · go · L243-L294 (52 LOC)
viz.go
func (m model) renderSort() string {
	if len(m.sortFrames) == 0 || m.sortWidth == 0 {
		return ""
	}

	frac := m.progressFraction()
	idx := int(frac * float64(len(m.sortFrames)-1))
	if idx >= len(m.sortFrames) {
		idx = len(m.sortFrames) - 1
	}

	frame := m.sortFrames[idx]
	total := len(frame)

	// Glow map: find most recent change per cell, decay over trail
	const trailLen = 8
	glow := make([]float64, total)
	for i := range frame {
		for j := idx; j > idx-trailLen && j > 0; j-- {
			if m.sortFrames[j][i] != m.sortFrames[j-1][i] {
				glow[i] = 1.0 - float64(idx-j)/float64(trailLen)
				break
			}
		}
	}

	var rows []string
	for rowStart := 0; rowStart < total; rowStart += m.sortWidth {
		end := rowStart + m.sortWidth
		if end > total {
			end = total
		}
		var row strings.Builder
		for i := rowStart; i < end; i++ {
			// Snap glow to 3 discrete lightness levels (no modifyColor)
			var l float64
			switch {
			case glow[i] > 0.66:
				l = 0.7
			case glow[i] > 0.33:
				l = 0.5
			defa
bubbleSortFrames function · go · L296-L311 (16 LOC)
viz.go
func bubbleSortFrames(arr []int) [][]int {
	a := make([]int, len(arr))
	copy(a, arr)

	frames := [][]int{append([]int(nil), a...)}
	n := len(a)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-1-i; j++ {
			if a[j] > a[j+1] {
				a[j], a[j+1] = a[j+1], a[j]
				frames = append(frames, append([]int(nil), a...))
			}
		}
	}
	return frames
}
mergeSortFrames function · go · L313-L320 (8 LOC)
viz.go
func mergeSortFrames(arr []int) [][]int {
	a := make([]int, len(arr))
	copy(a, arr)

	frames := [][]int{append([]int(nil), a...)}
	mergeSortRec(a, 0, len(a), &frames)
	return frames
}
mergeSortRec function · go · L322-L330 (9 LOC)
viz.go
func mergeSortRec(a []int, lo, hi int, frames *[][]int) {
	if hi-lo <= 1 {
		return
	}
	mid := (lo + hi) / 2
	mergeSortRec(a, lo, mid, frames)
	mergeSortRec(a, mid, hi, frames)
	mergeHalves(a, lo, mid, hi, frames)
}
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
mergeHalves function · go · L332-L362 (31 LOC)
viz.go
func mergeHalves(a []int, lo, mid, hi int, frames *[][]int) {
	left := make([]int, mid-lo)
	right := make([]int, hi-mid)
	copy(left, a[lo:mid])
	copy(right, a[mid:hi])

	i, j, k := 0, 0, lo
	for i < len(left) && j < len(right) {
		if left[i] <= right[j] {
			a[k] = left[i]
			i++
		} else {
			a[k] = right[j]
			j++
		}
		k++
		*frames = append(*frames, append([]int(nil), a...))
	}
	for i < len(left) {
		a[k] = left[i]
		i++
		k++
		*frames = append(*frames, append([]int(nil), a...))
	}
	for j < len(right) {
		a[k] = right[j]
		j++
		k++
		*frames = append(*frames, append([]int(nil), a...))
	}
}
quickSortFrames function · go · L364-L371 (8 LOC)
viz.go
func quickSortFrames(arr []int) [][]int {
	a := make([]int, len(arr))
	copy(a, arr)

	frames := [][]int{append([]int(nil), a...)}
	quickSortRec(a, 0, len(a)-1, &frames)
	return frames
}
quickSortRec function · go · L373-L390 (18 LOC)
viz.go
func quickSortRec(a []int, lo, hi int, frames *[][]int) {
	if lo >= hi {
		return
	}
	pivot := a[hi]
	i := lo
	for j := lo; j < hi; j++ {
		if a[j] < pivot {
			a[i], a[j] = a[j], a[i]
			*frames = append(*frames, append([]int(nil), a...))
			i++
		}
	}
	a[i], a[hi] = a[hi], a[i]
	*frames = append(*frames, append([]int(nil), a...))
	quickSortRec(a, lo, i-1, frames)
	quickSortRec(a, i+1, hi, frames)
}
page 1 / 2next ›