mirror of
https://github.com/charmbracelet/glow.git
synced 2026-08-24 00:54:17 +02:00
redesign: TUI theme picker with arrow keys, color swatches, and save/cancel
Replace numbered input with a Bubble Tea TUI: - Arrow keys (↑/↓ or j/k) to browse 11 themes - Color swatches show actual theme colors below the list - Enter to save, Esc/q to cancel, r for random palette - Live preview with color blocks for Text, H1, H1 Bg, H2, Code, Link
This commit is contained in:
parent
028b0f7bdb
commit
41fdc1869b
1 changed files with 235 additions and 199 deletions
450
style_cmd.go
450
style_cmd.go
|
|
@ -8,45 +8,47 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/bubbles/help"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type theme struct {
|
||||
Name string
|
||||
Description string
|
||||
Doc string
|
||||
H1 string
|
||||
H1Bg string
|
||||
H1Bold bool
|
||||
H2 string
|
||||
H3 string
|
||||
H6 string
|
||||
Code string
|
||||
CodeBg string
|
||||
CodeBlock string
|
||||
Link string
|
||||
LinkUnder bool
|
||||
HR string
|
||||
BqToken string
|
||||
Name string
|
||||
Doc string
|
||||
H1 string
|
||||
H1Bg string
|
||||
H1Bold bool
|
||||
H2 string
|
||||
H3 string
|
||||
H6 string
|
||||
Code string
|
||||
CodeBg string
|
||||
CodeBlock string
|
||||
Link string
|
||||
LinkUnder bool
|
||||
HR string
|
||||
BqToken string
|
||||
}
|
||||
|
||||
var themes = []theme{
|
||||
{Name: "Catppuccin Mocha", Description: "Dark, warm purple-pink tones", Doc: "252", H1: "228", H1Bg: "63", H1Bold: true, H2: "39", H3: "39", H6: "35", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "30", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Catppuccin Latte", Description: "Light, warm purple-pink tones", Doc: "234", H1: "228", H1Bg: "63", H1Bold: true, H2: "27", H3: "27", H6: "35", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "36", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Nord", Description: "Dark, arctic blue tones", Doc: "252", H1: "228", H1Bg: "67", H1Bold: true, H2: "110", H3: "110", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "110", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Gruvbox Dark", Description: "Dark, retro warm tones", Doc: "223", H1: "228", H1Bg: "88", H1Bold: true, H2: "214", H3: "214", H6: "108", Code: "203", CodeBg: "237", CodeBlock: "244", Link: "109", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Gruvbox Light", Description: "Light, retro warm tones", Doc: "237", H1: "228", H1Bg: "88", H1Bold: true, H2: "214", H3: "214", H6: "108", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "109", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Solarized Dark", Description: "Dark, sepia-toned", Doc: "252", H1: "228", H1Bg: "33", H1Bold: true, H2: "37", H3: "37", H6: "64", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "33", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Solarized Light", Description: "Light, sepia-toned", Doc: "234", H1: "228", H1Bg: "33", H1Bold: true, H2: "37", H3: "37", H6: "64", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "33", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Tokyo Night", Description: "Dark, vibrant neon", Doc: "252", H1: "228", H1Bg: "99", H1Bold: true, H2: "147", H3: "147", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "147", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Dracula", Description: "Dark, purple accents", Doc: "252", H1: "228", H1Bg: "99", H1Bold: true, H2: "212", H3: "212", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "212", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "One Dark", Description: "Dark, balanced blue-gray", Doc: "252", H1: "228", H1Bg: "67", H1Bold: true, H2: "75", H3: "75", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "75", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Random", Description: "Surprise me — auto-generated palette", Doc: "252", H1: "228", H1Bg: "63", H1Bold: true, H2: "39", H3: "39", H6: "35", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "30", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Catppuccin Mocha", Doc: "252", H1: "228", H1Bg: "63", H1Bold: true, H2: "39", H3: "39", H6: "35", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "30", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Catppuccin Latte", Doc: "234", H1: "228", H1Bg: "63", H1Bold: true, H2: "27", H3: "27", H6: "35", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "36", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Nord", Doc: "252", H1: "228", H1Bg: "67", H1Bold: true, H2: "110", H3: "110", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "110", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Gruvbox Dark", Doc: "223", H1: "228", H1Bg: "88", H1Bold: true, H2: "214", H3: "214", H6: "108", Code: "203", CodeBg: "237", CodeBlock: "244", Link: "109", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Gruvbox Light", Doc: "237", H1: "228", H1Bg: "88", H1Bold: true, H2: "214", H3: "214", H6: "108", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "109", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Solarized Dark", Doc: "252", H1: "228", H1Bg: "33", H1Bold: true, H2: "37", H3: "37", H6: "64", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "33", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Solarized Light", Doc: "234", H1: "228", H1Bg: "33", H1Bold: true, H2: "37", H3: "37", H6: "64", Code: "203", CodeBg: "254", CodeBlock: "242", Link: "33", LinkUnder: true, HR: "249", BqToken: "│ "},
|
||||
{Name: "Tokyo Night", Doc: "252", H1: "228", H1Bg: "99", H1Bold: true, H2: "147", H3: "147", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "147", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Dracula", Doc: "252", H1: "228", H1Bg: "99", H1Bold: true, H2: "212", H3: "212", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "212", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "One Dark", Doc: "252", H1: "228", H1Bg: "67", H1Bold: true, H2: "75", H3: "75", H6: "150", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "75", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
{Name: "Surprise Me", Doc: "252", H1: "228", H1Bg: "63", H1Bold: true, H2: "39", H3: "39", H6: "35", Code: "203", CodeBg: "236", CodeBlock: "244", Link: "30", LinkUnder: true, HR: "240", BqToken: "│ "},
|
||||
}
|
||||
|
||||
var styleOutputPath string
|
||||
var outputPath string
|
||||
|
||||
var styleCmd = &cobra.Command{
|
||||
Use: "style",
|
||||
|
|
@ -61,128 +63,221 @@ var styleInitCmd = &cobra.Command{
|
|||
Use: "init",
|
||||
Short: "Create a color style from a theme palette",
|
||||
Long: paragraph(fmt.Sprintf(
|
||||
"\nChoose from preset color themes or generate a random palette. Preview the result and save it.\n\nThe generated file can be used with %s.",
|
||||
"\nPick from preset color themes using arrow keys, preview the colors, and save.\nThe generated file can be used with %s.",
|
||||
keyword("glow -s path/to/style.json"),
|
||||
)),
|
||||
Example: paragraph(" glow style init\n glow style init -o ~/mytheme.json"),
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
return runStyleInit()
|
||||
return runStyleInitTUI()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
styleInitCmd.Flags().StringVarP(&styleOutputPath, "output", "o", "", "output path for the style JSON (default ~/.config/glow/style.json)")
|
||||
styleInitCmd.Flags().StringVarP(&outputPath, "output", "o", "", "output path for the style JSON (default ~/.config/glow/style.json)")
|
||||
styleCmd.AddCommand(styleInitCmd)
|
||||
}
|
||||
|
||||
func runStyleInit() error {
|
||||
if styleOutputPath == "" {
|
||||
type keyMap struct {
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
Enter key.Binding
|
||||
Random key.Binding
|
||||
Quit key.Binding
|
||||
}
|
||||
|
||||
func (k keyMap) ShortHelp() []key.Binding {
|
||||
return []key.Binding{k.Up, k.Down, k.Enter, k.Random, k.Quit}
|
||||
}
|
||||
|
||||
func (k keyMap) FullHelp() [][]key.Binding {
|
||||
return [][]key.Binding{{k.Up, k.Down, k.Enter, k.Random, k.Quit}}
|
||||
}
|
||||
|
||||
var keys = keyMap{
|
||||
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up")),
|
||||
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
||||
Enter: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "save")),
|
||||
Random: key.NewBinding(key.WithKeys("r"), key.WithHelp("r", "random")),
|
||||
Quit: key.NewBinding(key.WithKeys("esc", "q", "ctrl+c"), key.WithHelp("esc/q", "cancel")),
|
||||
}
|
||||
|
||||
type model struct {
|
||||
themes []theme
|
||||
cursor int
|
||||
result *result
|
||||
quitting bool
|
||||
help help.Model
|
||||
}
|
||||
|
||||
type result struct {
|
||||
styleJSON []byte
|
||||
themeName string
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.help.Width = msg.Width
|
||||
return m, nil
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, keys.Quit):
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
case key.Matches(msg, keys.Up):
|
||||
m.cursor--
|
||||
if m.cursor < 0 {
|
||||
m.cursor = len(m.themes) - 1
|
||||
}
|
||||
case key.Matches(msg, keys.Down):
|
||||
m.cursor++
|
||||
if m.cursor >= len(m.themes) {
|
||||
m.cursor = 0
|
||||
}
|
||||
case key.Matches(msg, keys.Random):
|
||||
rt := randomTheme()
|
||||
m.themes = append(m.themes[:len(m.themes)-1], rt)
|
||||
m.cursor = len(m.themes) - 1
|
||||
case key.Matches(msg, keys.Enter):
|
||||
t := m.themes[m.cursor]
|
||||
data, _ := json.MarshalIndent(buildStyle(t.Doc, t.H1, t.H1Bg, t.H1Bold, t.H2, t.H3, t.H6,
|
||||
t.Code, t.CodeBg, t.CodeBlock, t.Link, t.LinkUnder, t.HR, t.BqToken), "", " ")
|
||||
m.result = &result{styleJSON: data, themeName: t.Name}
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.quitting {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := m.themes[m.cursor]
|
||||
|
||||
title := lipStyles.title.Render("Pick a theme")
|
||||
divider := strings.Repeat("─", 70)
|
||||
|
||||
var listRows []string
|
||||
for i, th := range m.themes {
|
||||
prefix := " "
|
||||
nameStyle := lipStyles.item
|
||||
if i == m.cursor {
|
||||
prefix = lipStyles.cursor.Render("▸")
|
||||
nameStyle = lipStyles.selected
|
||||
}
|
||||
row := fmt.Sprintf("%s %s", prefix, nameStyle.Render(th.Name))
|
||||
listRows = append(listRows, row)
|
||||
}
|
||||
themeList := lipgloss.JoinVertical(lipgloss.Left, listRows...)
|
||||
|
||||
swatch := func(label, color string) string {
|
||||
block := lipgloss.NewStyle().Background(lipgloss.Color(color)).Render(" ")
|
||||
return lipStyles.swatchLabel.Render(fmt.Sprintf("%s %s", block, label))
|
||||
}
|
||||
swatchRow := fmt.Sprintf(
|
||||
" %s %s %s %s %s %s",
|
||||
swatch("Text", t.Doc),
|
||||
swatch("H1", t.H1),
|
||||
swatch("H1 Bg", t.H1Bg),
|
||||
swatch("H2", t.H2),
|
||||
swatch("Code", t.Code),
|
||||
swatch("Link", t.Link),
|
||||
)
|
||||
|
||||
helpView := m.help.View(keys)
|
||||
|
||||
content := fmt.Sprintf(
|
||||
"%s\n%s\n\n%s\n\n %s\n %s\n\n%s\n",
|
||||
title,
|
||||
divider,
|
||||
themeList,
|
||||
lipStyles.previewTitle.Render("Colors"),
|
||||
swatchRow,
|
||||
helpView,
|
||||
)
|
||||
|
||||
return lipStyles.app.Render(content)
|
||||
}
|
||||
|
||||
func runStyleInitTUI() error {
|
||||
if outputPath == "" {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to determine home directory: %w", err)
|
||||
}
|
||||
styleOutputPath = filepath.Join(home, ".config", "glow", "style.json")
|
||||
outputPath = filepath.Join(home, ".config", "glow", "style.json")
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Println()
|
||||
fmt.Println(" " + keyword("Pick a theme"))
|
||||
fmt.Println(" " + strings.Repeat("─", 50))
|
||||
for i, t := range themes {
|
||||
fmt.Printf(" %2d) %-20s %s\n", i+1, t.Name, t.Description)
|
||||
}
|
||||
fmt.Printf(" %2d) %-20s %s\n", 0, "Quit", "exit without saving")
|
||||
fmt.Print("\n Your choice [1]: ")
|
||||
|
||||
var choice int
|
||||
input := ""
|
||||
_, _ = fmt.Scanln(&input)
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
choice = 1
|
||||
} else {
|
||||
_, err := fmt.Sscanf(input, "%d", &choice)
|
||||
if err != nil || choice < 0 || choice > len(themes) {
|
||||
fmt.Println(" Invalid choice. Try again.")
|
||||
continue
|
||||
}
|
||||
}
|
||||
if choice == 0 {
|
||||
fmt.Println(" " + keyword("Bye!"))
|
||||
return nil
|
||||
}
|
||||
|
||||
t := themes[choice-1]
|
||||
if t.Name == "Random" {
|
||||
t = randomTheme()
|
||||
}
|
||||
|
||||
style := buildStyle(t.Doc, t.H1, t.H1Bg, t.H1Bold, t.H2, t.H3, t.H6,
|
||||
t.Code, t.CodeBg, t.CodeBlock, t.Link, t.LinkUnder, t.HR, t.BqToken)
|
||||
|
||||
data, err := json.MarshalIndent(style, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal style: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(" " + keyword(t.Name))
|
||||
fmt.Println(" " + strings.Repeat("─", 50))
|
||||
|
||||
showPreview(data)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(" " + strings.Repeat("─", 50))
|
||||
fmt.Print(" [S]ave [R]andomize [Q]uit [S]: ")
|
||||
|
||||
var action string
|
||||
_, _ = fmt.Scanln(&action)
|
||||
action = strings.TrimSpace(strings.ToLower(action))
|
||||
|
||||
switch action {
|
||||
case "", "s", "save":
|
||||
if err := os.MkdirAll(filepath.Dir(styleOutputPath), 0o700); err != nil {
|
||||
return fmt.Errorf("unable to create directory: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(styleOutputPath, data, 0o600); err != nil {
|
||||
return fmt.Errorf("unable to write style file: %w", err)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println(" ✓ Style saved to: " + styleOutputPath)
|
||||
fmt.Println(" Use it with: " + keyword("glow -s "+styleOutputPath))
|
||||
fmt.Println()
|
||||
return nil
|
||||
case "r", "random", "rand":
|
||||
continue
|
||||
case "q", "quit":
|
||||
fmt.Println(" " + keyword("Bye!"))
|
||||
return nil
|
||||
default:
|
||||
continue
|
||||
}
|
||||
m := model{
|
||||
themes: themes,
|
||||
help: help.New(),
|
||||
}
|
||||
|
||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
||||
final, err := p.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m = final.(model)
|
||||
if m.result == nil {
|
||||
fmt.Println(" Cancelled.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(outputPath), 0o700); err != nil {
|
||||
return fmt.Errorf("unable to create directory: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(outputPath, m.result.styleJSON, 0o600); err != nil {
|
||||
return fmt.Errorf("unable to write style file: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf(" ✓ %s saved to: %s\n", m.result.themeName, outputPath)
|
||||
fmt.Printf(" Use it with: %s\n", keyword("glow -s "+outputPath))
|
||||
return nil
|
||||
}
|
||||
|
||||
func showPreview(data []byte) {
|
||||
sample := "# Hello World\n\nThis is **bold** and *italic* text. Here is `inline code`.\n\n## Code Block\n\n```go\nfunc hello() {\n\tfmt.Println(\"Hello, World!\")\n}\n```\n\n> A wise blockquote once said...\n\n---\n\n[Link to somewhere](https://example.com)"
|
||||
type styleSet struct {
|
||||
app lipgloss.Style
|
||||
title lipgloss.Style
|
||||
item lipgloss.Style
|
||||
selected lipgloss.Style
|
||||
cursor lipgloss.Style
|
||||
previewTitle lipgloss.Style
|
||||
swatchLabel lipgloss.Style
|
||||
}
|
||||
|
||||
r, err := glamour.NewTermRenderer(
|
||||
glamour.WithStylesFromJSONBytes(data),
|
||||
glamour.WithWordWrap(60),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(" (preview unavailable)")
|
||||
return
|
||||
}
|
||||
defer r.Close() //nolint:errcheck
|
||||
var lipStyles styleSet
|
||||
|
||||
out, err := r.Render(sample)
|
||||
if err != nil {
|
||||
fmt.Println(" (preview unavailable)")
|
||||
return
|
||||
func init() {
|
||||
lipStyles = styleSet{
|
||||
app: lipgloss.NewStyle().
|
||||
Padding(1, 2),
|
||||
title: lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#04B575")).
|
||||
Padding(0, 0, 1, 0),
|
||||
item: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#A49FA5")),
|
||||
selected: lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#FFFDF5")),
|
||||
cursor: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#04B575")),
|
||||
previewTitle: lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#777777")),
|
||||
swatchLabel: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#979797")),
|
||||
}
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
||||
func randomTheme() theme {
|
||||
|
|
@ -191,7 +286,7 @@ func randomTheme() theme {
|
|||
colors[i] = fmt.Sprintf("%d", rand.Intn(256))
|
||||
}
|
||||
return theme{
|
||||
Name: "Random",
|
||||
Name: "Surprise Me",
|
||||
Doc: colors[0],
|
||||
H1: colors[1],
|
||||
H1Bg: colors[2],
|
||||
|
|
@ -209,105 +304,46 @@ func randomTheme() theme {
|
|||
}
|
||||
}
|
||||
|
||||
func promptColor(label, defaultColor string, isDark bool) string {
|
||||
hint := defaultColor
|
||||
fmt.Printf(" %s [%s]: ", label, hint)
|
||||
var input string
|
||||
_, _ = fmt.Scanln(&input)
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
return defaultColor
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func buildStyle(docColor, h1Color, h1Bg string, h1Bold bool, h2Color, h3Color, h6Color string,
|
||||
codeColor, codeBg, codeBlockColor, linkColor string, linkUnderline bool, hrColor, blockQuoteToken string) map[string]interface{} {
|
||||
|
||||
return map[string]interface{}{
|
||||
"document": map[string]interface{}{
|
||||
"block_prefix": "\n",
|
||||
"block_suffix": "\n",
|
||||
"color": docColor,
|
||||
"margin": 2,
|
||||
"block_prefix": "\n", "block_suffix": "\n", "color": docColor, "margin": 2,
|
||||
},
|
||||
"block_quote": map[string]interface{}{
|
||||
"indent": 1,
|
||||
"indent_token": blockQuoteToken,
|
||||
"indent": 1, "indent_token": blockQuoteToken,
|
||||
},
|
||||
"paragraph": map[string]interface{}{},
|
||||
"list": map[string]interface{}{
|
||||
"level_indent": 2,
|
||||
},
|
||||
"list": map[string]interface{}{"level_indent": 2},
|
||||
"heading": map[string]interface{}{
|
||||
"block_suffix": "\n",
|
||||
"color": h2Color,
|
||||
"bold": true,
|
||||
"block_suffix": "\n", "color": h2Color, "bold": true,
|
||||
},
|
||||
"h1": map[string]interface{}{
|
||||
"prefix": " ",
|
||||
"suffix": " ",
|
||||
"color": h1Color,
|
||||
"background_color": h1Bg,
|
||||
"bold": h1Bold,
|
||||
},
|
||||
"h2": map[string]interface{}{
|
||||
"prefix": "## ",
|
||||
},
|
||||
"h3": map[string]interface{}{
|
||||
"prefix": "### ",
|
||||
"color": h3Color,
|
||||
},
|
||||
"h4": map[string]interface{}{
|
||||
"prefix": "#### ",
|
||||
},
|
||||
"h5": map[string]interface{}{
|
||||
"prefix": "##### ",
|
||||
},
|
||||
"h6": map[string]interface{}{
|
||||
"prefix": "###### ",
|
||||
"color": h6Color,
|
||||
"bold": false,
|
||||
"prefix": " ", "suffix": " ", "color": h1Color, "background_color": h1Bg, "bold": h1Bold,
|
||||
},
|
||||
"h2": map[string]interface{}{"prefix": "## "},
|
||||
"h3": map[string]interface{}{"prefix": "### ", "color": h3Color},
|
||||
"h4": map[string]interface{}{"prefix": "#### "},
|
||||
"h5": map[string]interface{}{"prefix": "##### "},
|
||||
"h6": map[string]interface{}{"prefix": "###### ", "color": h6Color, "bold": false},
|
||||
"text": map[string]interface{}{},
|
||||
"strikethrough": map[string]interface{}{"crossed_out": true},
|
||||
"emph": map[string]interface{}{"italic": true},
|
||||
"strong": map[string]interface{}{"bold": true},
|
||||
"hr": map[string]interface{}{
|
||||
"color": hrColor,
|
||||
"format": "\n--------\n",
|
||||
},
|
||||
"item": map[string]interface{}{"block_prefix": "• "},
|
||||
"enumeration": map[string]interface{}{"block_prefix": ". "},
|
||||
"task": map[string]interface{}{
|
||||
"ticked": "[✓] ",
|
||||
"unticked": "[ ] ",
|
||||
},
|
||||
"link": map[string]interface{}{
|
||||
"color": linkColor,
|
||||
"underline": linkUnderline,
|
||||
},
|
||||
"link_text": map[string]interface{}{
|
||||
"color": "35",
|
||||
"bold": true,
|
||||
},
|
||||
"image": map[string]interface{}{
|
||||
"color": "212",
|
||||
"underline": true,
|
||||
},
|
||||
"image_text": map[string]interface{}{
|
||||
"color": "243",
|
||||
"format": "Image: {{.text}} →",
|
||||
},
|
||||
"hr": map[string]interface{}{"color": hrColor, "format": "\n--------\n"},
|
||||
"item": map[string]interface{}{"block_prefix": "• "},
|
||||
"enumeration": map[string]interface{}{"block_prefix": ". "},
|
||||
"task": map[string]interface{}{"ticked": "[✓] ", "unticked": "[ ] "},
|
||||
"link": map[string]interface{}{"color": linkColor, "underline": linkUnderline},
|
||||
"link_text": map[string]interface{}{"color": "35", "bold": true},
|
||||
"image": map[string]interface{}{"color": "212", "underline": true},
|
||||
"image_text": map[string]interface{}{"color": "243", "format": "Image: {{.text}} →"},
|
||||
"code": map[string]interface{}{
|
||||
"prefix": "\u00a0",
|
||||
"suffix": "\u00a0",
|
||||
"color": codeColor,
|
||||
"background_color": codeBg,
|
||||
"prefix": "\u00a0", "suffix": "\u00a0", "color": codeColor, "background_color": codeBg,
|
||||
},
|
||||
"code_block": map[string]interface{}{
|
||||
"color": codeBlockColor,
|
||||
"margin": 2,
|
||||
"color": codeBlockColor, "margin": 2,
|
||||
"chroma": map[string]interface{}{
|
||||
"text": map[string]interface{}{"color": "#C4C4C4"},
|
||||
"error": map[string]interface{}{"color": "#F1F1F1", "background_color": "#F05B5B"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue