v0.19.0 d7aa01a

util.go RawHistoryFolder

1package bot
2
3import (
4	"strings"
5	"unicode/utf8"
6)
7
8// ChunkMessage splits s into chunks no larger than limit runes, on line
9// boundaries when possible.
10func ChunkMessage(s string, limit int) []string {
11	if limit <= 0 || utf8.RuneCountInString(s) <= limit {
12		return []string{s}
13	}
14	var out []string
15	lines := strings.Split(s, "\n")
16	var cur strings.Builder
17	curRunes := 0
18	for _, l := range lines {
19		lineRunes := utf8.RuneCountInString(l)
20		sepRunes := 0
21		if curRunes > 0 {
22			sepRunes = 1
23		}
24		if curRunes+sepRunes+lineRunes > limit && curRunes > 0 {
25			out = append(out, cur.String())
26			cur.Reset()
27			curRunes = 0
28			sepRunes = 0
29		}
30		if lineRunes > limit {
31			// Line itself too long; hard-split on rune boundaries.
32			for lineRunes > limit {
33				i := byteIndexAfterRunes(l, limit)
34				out = append(out, l[:i])
35				l = l[i:]
36				lineRunes = utf8.RuneCountInString(l)
37			}
38		}
39		if curRunes > 0 {
40			cur.WriteString("\n")
41			curRunes++
42		}
43		cur.WriteString(l)
44		curRunes += utf8.RuneCountInString(l)
45	}
46	if curRunes > 0 {
47		out = append(out, cur.String())
48	}
49	return out
50}
51
52func byteIndexAfterRunes(s string, n int) int {
53	if n <= 0 {
54		return 0
55	}
56	count := 0
57	for i := range s {
58		if count == n {
59			return i
60		}
61		count++
62	}
63	return len(s)
64}
65
66// IsImageMIME returns true for MIME types the model can probably ingest
67// as a vision input.
68func IsImageMIME(m string) bool {
69	switch strings.ToLower(m) {
70	case "image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp":
71		return true
72	}
73	return false
74}
75
76// GuessImageMIME infers a mime type from a filename suffix. Falls back
77// to image/jpeg because telegram photos are always re-encoded to jpeg
78// but getFile's file_path may omit the extension.
79func GuessImageMIME(path string) string {
80	lower := strings.ToLower(path)
81	switch {
82	case strings.HasSuffix(lower, ".png"):
83		return "image/png"
84	case strings.HasSuffix(lower, ".jpg"), strings.HasSuffix(lower, ".jpeg"):
85		return "image/jpeg"
86	case strings.HasSuffix(lower, ".gif"):
87		return "image/gif"
88	case strings.HasSuffix(lower, ".webp"):
89		return "image/webp"
90	}
91	return "image/jpeg"
92}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close