mirror of
https://github.com/Kyren223/eko.git
synced 2025-09-05 21:18:14 +00:00
Refactored client side to comply with protocol changes, implemnted tos
screen model, still missing accept/decline and authentication
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"github.com/kyren223/eko/certs"
|
||||
"github.com/kyren223/eko/embeds"
|
||||
"github.com/kyren223/eko/internal/client/config"
|
||||
"github.com/kyren223/eko/internal/client/ui"
|
||||
"github.com/kyren223/eko/internal/packet"
|
||||
@@ -30,36 +30,36 @@ var (
|
||||
)
|
||||
|
||||
type (
|
||||
ConnectionEstablished snowflake.ID
|
||||
ConnectionFailed error
|
||||
ConnectionLost error
|
||||
ConnectionClosed struct{}
|
||||
AuthenticationEstablished snowflake.ID
|
||||
ConnectionEstablished struct{}
|
||||
ConnectionFailed error
|
||||
ConnectionLost error
|
||||
ConnectionClosed struct{}
|
||||
)
|
||||
|
||||
func Connect(privKey ed25519.PrivateKey, timeout time.Duration) tea.Cmd {
|
||||
func Connect(timeout time.Duration) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
id, err := connect(ctx, privKey)
|
||||
err := connect(ctx)
|
||||
if err != nil {
|
||||
return ConnectionFailed(err)
|
||||
}
|
||||
return ConnectionEstablished(id)
|
||||
return ConnectionEstablished{}
|
||||
}
|
||||
}
|
||||
|
||||
func connect(ctx context.Context, privKey ed25519.PrivateKey) (snowflake.ID, error) {
|
||||
func connect(ctx context.Context) error {
|
||||
assert.Assert(conn == nil, "cannot connect, connection is active")
|
||||
closed = false
|
||||
|
||||
var id snowflake.ID
|
||||
connChan := make(chan net.Conn, 1)
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
framer = packet.NewFramer()
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(certs.CertPEM) {
|
||||
if !certPool.AppendCertsFromPEM(embeds.ServerCertificate) {
|
||||
log.Fatalln("failed to append server certificate")
|
||||
}
|
||||
|
||||
@@ -85,11 +85,6 @@ func connect(ctx context.Context, privKey ed25519.PrivateKey) (snowflake.ID, err
|
||||
}
|
||||
log.Println("established connection with the server")
|
||||
|
||||
if id, err = handleAuth(ctx, connection, privKey); err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
log.Println("successfully authenticated with the server")
|
||||
connChan <- connection
|
||||
}()
|
||||
|
||||
@@ -97,15 +92,15 @@ func connect(ctx context.Context, privKey ed25519.PrivateKey) (snowflake.ID, err
|
||||
case connection := <-connChan:
|
||||
conn = connection
|
||||
case err := <-errChan:
|
||||
return 0, err
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
go readForever(conn)
|
||||
go handlePacketStream()
|
||||
|
||||
return id, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAuth(ctx context.Context, conn net.Conn, privKey ed25519.PrivateKey) (snowflake.ID, error) {
|
||||
|
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/kyren223/eko/internal/client/ui/core/state"
|
||||
"github.com/kyren223/eko/internal/client/ui/core/usersettings"
|
||||
"github.com/kyren223/eko/internal/client/ui/loadscreen"
|
||||
"github.com/kyren223/eko/internal/client/ui/tosscreen"
|
||||
"github.com/kyren223/eko/internal/client/ui/viminput"
|
||||
"github.com/kyren223/eko/internal/data"
|
||||
"github.com/kyren223/eko/internal/packet"
|
||||
@@ -40,12 +41,12 @@ import (
|
||||
"github.com/kyren223/eko/pkg/snowflake"
|
||||
)
|
||||
|
||||
var (
|
||||
connectingToServer = "Connecting to server.."
|
||||
connectionFailed = "Connection failed - retrying in %d sec..."
|
||||
connectionTimeout = 5 * time.Second
|
||||
initialTimeout = 3750 * time.Millisecond
|
||||
timerInterval = 50 * time.Millisecond
|
||||
const (
|
||||
ConnectingToServer = "Connecting to server.."
|
||||
ConnectionFailed = "Connection failed - retrying in %d sec..."
|
||||
ConnectionTimeout = 5 * time.Second
|
||||
InitialTimeout = 3750 * time.Millisecond
|
||||
TimerInterval = 50 * time.Millisecond
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -62,14 +63,25 @@ const (
|
||||
FocusMax
|
||||
)
|
||||
|
||||
type State int
|
||||
|
||||
const (
|
||||
Disconnected State = iota
|
||||
Connected
|
||||
ConnectedReceivedTos
|
||||
ConnectedAcceptedTos
|
||||
Authenticated
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
name string
|
||||
privKey ed25519.PrivateKey
|
||||
|
||||
loading loadscreen.Model
|
||||
timer timer.Model
|
||||
timeout time.Duration
|
||||
connected bool
|
||||
loading loadscreen.Model
|
||||
tos *tosscreen.Model
|
||||
timer timer.Model
|
||||
timeout time.Duration
|
||||
state State
|
||||
|
||||
helpPopup *HelpPopup
|
||||
userSettingsPopup *usersettings.Model
|
||||
@@ -94,10 +106,11 @@ func New(privKey ed25519.PrivateKey, name string) Model {
|
||||
m := Model{
|
||||
name: name,
|
||||
privKey: privKey,
|
||||
loading: loadscreen.New(connectingToServer),
|
||||
timer: newTimer(initialTimeout),
|
||||
timeout: initialTimeout,
|
||||
connected: false,
|
||||
loading: loadscreen.New(ConnectingToServer),
|
||||
tos: nil,
|
||||
timer: newTimer(InitialTimeout),
|
||||
timeout: InitialTimeout,
|
||||
state: Disconnected,
|
||||
helpPopup: nil,
|
||||
userSettingsPopup: nil,
|
||||
networkCreationPopup: nil,
|
||||
@@ -108,6 +121,7 @@ func New(privKey ed25519.PrivateKey, name string) Model {
|
||||
banReasonPopup: nil,
|
||||
banViewPopup: nil,
|
||||
signalAddPopup: nil,
|
||||
profilePopup: nil,
|
||||
networkList: networklist.New(),
|
||||
signalList: signallist.New(),
|
||||
frequencyList: frequencylist.New(),
|
||||
@@ -121,12 +135,29 @@ func New(privKey ed25519.PrivateKey, name string) Model {
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return tea.Batch(gateway.Connect(m.privKey, connectionTimeout), m.loading.Init())
|
||||
return tea.Batch(gateway.Connect(ConnectionTimeout), m.loading.Init())
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
if !m.connected {
|
||||
switch m.state {
|
||||
case Disconnected:
|
||||
return m.loading.View()
|
||||
|
||||
case Connected:
|
||||
return m.loading.View()
|
||||
case ConnectedReceivedTos:
|
||||
assert.NotNil(m.tos, "TOS is expected to not be nil if received TOS already")
|
||||
return m.tos.View()
|
||||
|
||||
case ConnectedAcceptedTos:
|
||||
// TODO: auth
|
||||
return m.loading.View()
|
||||
|
||||
case Authenticated:
|
||||
// Continue
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected core.State: %#v", m.state))
|
||||
}
|
||||
|
||||
if m.HasPopup() {
|
||||
@@ -188,36 +219,52 @@ func (m Model) View() string {
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.connected {
|
||||
switch m.state {
|
||||
|
||||
case Disconnected:
|
||||
cmd := m.updateDisconnected(msg)
|
||||
return m, cmd
|
||||
|
||||
case Connected:
|
||||
fallthrough
|
||||
case ConnectedReceivedTos:
|
||||
fallthrough
|
||||
case ConnectedAcceptedTos:
|
||||
cmd := m.updateConnected(msg)
|
||||
return m, cmd
|
||||
} else {
|
||||
cmd := m.updateNotConnected(msg)
|
||||
|
||||
case Authenticated:
|
||||
cmd := m.updateAuthenticated(msg)
|
||||
return m, cmd
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected core.State: %#v", m.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) updateNotConnected(msg tea.Msg) tea.Cmd {
|
||||
func (m *Model) updateDisconnected(msg tea.Msg) tea.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case gateway.ConnectionEstablished:
|
||||
state.UserID = (*snowflake.ID)(&msg)
|
||||
m.connected = true
|
||||
m.timeout = initialTimeout
|
||||
m.state = Connected
|
||||
m.timeout = InitialTimeout
|
||||
|
||||
var setName tea.Cmd
|
||||
if m.name != "" {
|
||||
setName = gateway.Send(&packet.SetUserData{
|
||||
Data: nil,
|
||||
User: &data.User{
|
||||
Name: m.name,
|
||||
Description: "",
|
||||
IsPublicDM: true,
|
||||
},
|
||||
})
|
||||
m.name = ""
|
||||
}
|
||||
|
||||
return tea.Batch(m.timer.Stop(), setName)
|
||||
// TODO:
|
||||
// // state.UserID = (*snowflake.ID)(&msg)
|
||||
// var setName tea.Cmd
|
||||
// if m.name != "" {
|
||||
// setName = gateway.Send(&packet.SetUserData{
|
||||
// Data: nil,
|
||||
// User: &data.User{
|
||||
// Name: m.name,
|
||||
// Description: "",
|
||||
// IsPublicDM: true,
|
||||
// },
|
||||
// })
|
||||
// m.name = ""
|
||||
// }
|
||||
//
|
||||
// return tea.Batch(m.timer.Stop(), setName)
|
||||
return m.timer.Stop()
|
||||
|
||||
case gateway.ConnectionFailed:
|
||||
log.Println("failed to connect:", msg)
|
||||
@@ -227,8 +274,8 @@ func (m *Model) updateNotConnected(msg tea.Msg) tea.Cmd {
|
||||
|
||||
case timer.TimeoutMsg:
|
||||
m.timeout = min(m.timeout*2, time.Minute)
|
||||
m.loading.SetContent(connectingToServer)
|
||||
return gateway.Connect(m.privKey, connectionTimeout)
|
||||
m.loading.SetContent(ConnectingToServer)
|
||||
return gateway.Connect(ConnectionTimeout)
|
||||
|
||||
case timer.StartStopMsg:
|
||||
var cmd tea.Cmd
|
||||
@@ -252,6 +299,54 @@ func (m *Model) updateNotConnected(msg tea.Msg) tea.Cmd {
|
||||
}
|
||||
|
||||
func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
|
||||
switch msg := message.(type) {
|
||||
|
||||
case gateway.ConnectionLost:
|
||||
m.state = Disconnected
|
||||
m.timeout = InitialTimeout
|
||||
return tea.Batch(gateway.Connect(ConnectionTimeout), m.loading.Init())
|
||||
|
||||
case *packet.TosInfo:
|
||||
m.state = ConnectedReceivedTos
|
||||
combined := msg.Tos + "\n" + msg.PrivacyPolicy
|
||||
tos := tosscreen.New(combined)
|
||||
m.tos = &tos
|
||||
// var setName tea.Cmd
|
||||
// if m.name != "" {
|
||||
// setName = gateway.Send(&packet.SetUserData{
|
||||
// Data: nil,
|
||||
// User: &data.User{
|
||||
// Name: m.name,
|
||||
// Description: "",
|
||||
// IsPublicDM: true,
|
||||
// },
|
||||
// })
|
||||
// m.name = ""
|
||||
// }
|
||||
//
|
||||
// return tea.Batch(m.timer.Stop(), setName)
|
||||
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "enter":
|
||||
if m.state == ConnectedReceivedTos {
|
||||
// TODO:
|
||||
m.state = ConnectedAcceptedTos
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if m.state == ConnectedReceivedTos {
|
||||
tos, cmd := m.tos.Update(message)
|
||||
m.tos = &tos
|
||||
return cmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) updateAuthenticated(message tea.Msg) tea.Cmd {
|
||||
totalWidth := max(ui.Width, ui.MinWidth)
|
||||
totalWidth -= NetworkWidth
|
||||
sidebarWidth := int(math.Round(float64(totalWidth) * SidebarPercentage))
|
||||
@@ -274,9 +369,9 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
|
||||
|
||||
case gateway.ConnectionLost:
|
||||
state.UserID = nil
|
||||
m.connected = false
|
||||
m.timeout = initialTimeout
|
||||
return tea.Batch(gateway.Connect(m.privKey, connectionTimeout), m.loading.Init())
|
||||
m.state = Disconnected
|
||||
m.timeout = InitialTimeout
|
||||
return tea.Batch(gateway.Connect(ConnectionTimeout), m.loading.Init())
|
||||
|
||||
case *packet.Error:
|
||||
err := "new connection from another location, closing this one"
|
||||
@@ -619,11 +714,11 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
|
||||
|
||||
func (m *Model) updateLoadScreenContent() {
|
||||
seconds := m.timer.Timeout.Round(time.Second) / time.Second
|
||||
m.loading.SetContent(fmt.Sprintf(connectionFailed, seconds))
|
||||
m.loading.SetContent(fmt.Sprintf(ConnectionFailed, seconds))
|
||||
}
|
||||
|
||||
func newTimer(timeout time.Duration) timer.Model {
|
||||
return timer.NewWithInterval(timeout.Truncate(time.Second)+(time.Second/2), timerInterval)
|
||||
return timer.NewWithInterval(timeout.Truncate(time.Second)+(time.Second/2), TimerInterval)
|
||||
}
|
||||
|
||||
func (m *Model) move(direction int) {
|
||||
@@ -814,3 +909,7 @@ func getSignalNotification(signal snowflake.ID) int {
|
||||
|
||||
return pings
|
||||
}
|
||||
|
||||
func (m Model) ViewTos() string {
|
||||
return "TODO TOS"
|
||||
}
|
||||
|
332
internal/client/ui/tosscreen/tosscreen.go
Normal file
332
internal/client/ui/tosscreen/tosscreen.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package tosscreen
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/glamour/ansi"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
||||
// "github.com/charmbracelet/glamour"
|
||||
"github.com/kyren223/eko/internal/client/ui"
|
||||
"github.com/kyren223/eko/internal/client/ui/colors"
|
||||
"github.com/kyren223/eko/pkg/assert"
|
||||
)
|
||||
|
||||
const MarginPercentage = 0.25
|
||||
|
||||
var style = func() lipgloss.Style {
|
||||
return lipgloss.NewStyle().
|
||||
Border(lipgloss.ThickBorder()).
|
||||
BorderBackground(colors.Background).
|
||||
Background(colors.Background).
|
||||
MarginBackground(colors.BackgroundDimmer).
|
||||
Padding(0, 3).
|
||||
Margin(1, int(float32(ui.Width)*MarginPercentage), 0)
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
content string
|
||||
vp viewport.Model
|
||||
}
|
||||
|
||||
func New(content string) Model {
|
||||
m := Model{
|
||||
content: content,
|
||||
vp: viewport.New(ui.Width, ui.Height),
|
||||
}
|
||||
m.SetContent(content)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
footer := "-- Accept | ^C Decline | ↑↓ or J/K Scroll | ^D/^U PgDn/PgUp --"
|
||||
|
||||
fWidth := lipgloss.Width(footer)
|
||||
paddingLeft := (ui.Width - fWidth) / 2
|
||||
paddingRight := ui.Width - fWidth - paddingLeft
|
||||
footerStyle := lipgloss.NewStyle().
|
||||
Padding(0, paddingLeft, 0, paddingRight).
|
||||
Background(colors.BackgroundDimmer).
|
||||
MarginBackground(colors.BackgroundDimmer)
|
||||
footer = footerStyle.Render(footer)
|
||||
|
||||
view := m.vp.View()
|
||||
|
||||
return lipgloss.JoinVertical(lipgloss.Center, view, footer)
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
if m.vp.Width != ui.Width {
|
||||
m.vp.Width = ui.Width
|
||||
m.updateContent()
|
||||
}
|
||||
if m.vp.Height != ui.Height-1 {
|
||||
m.vp.Height = ui.Height - 1
|
||||
m.updateContent()
|
||||
}
|
||||
|
||||
m.vp.Style = style()
|
||||
m.vp, cmd = m.vp.Update(msg)
|
||||
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m *Model) updateContent() {
|
||||
style := style()
|
||||
margin := style.GetMarginLeft() + style.GetMarginRight()
|
||||
padding := style.GetPaddingLeft() + style.GetPaddingRight()
|
||||
border := 2
|
||||
lineWidth := ui.Width - margin - padding - border
|
||||
|
||||
r, _ := glamour.NewTermRenderer(
|
||||
glamour.WithStyles(MdStyle()),
|
||||
glamour.WithWordWrap(lineWidth),
|
||||
)
|
||||
content := m.content
|
||||
content = strings.ReplaceAll(content, "@", "") // HACK: workaround to remove mailto links
|
||||
content, err := r.Render(content)
|
||||
assert.NoError(err, "this should never error")
|
||||
content = strings.ReplaceAll(content, "", "@") // HACK: workaround to remove mailto links
|
||||
m.vp.SetContent(content)
|
||||
}
|
||||
|
||||
func (m *Model) SetContent(content string) {
|
||||
m.content = content
|
||||
m.updateContent()
|
||||
}
|
||||
|
||||
func MdStyle() ansi.StyleConfig {
|
||||
mdStyle := ansi.StyleConfig{
|
||||
Document: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
BlockPrefix: "\n",
|
||||
BlockSuffix: "\n",
|
||||
Color: stringPtr(colors.White),
|
||||
BackgroundColor: stringPtr(colors.Background),
|
||||
},
|
||||
Margin: uintPtr(defaultMargin),
|
||||
},
|
||||
BlockQuote: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{},
|
||||
Indent: uintPtr(1),
|
||||
IndentToken: stringPtr("│ "),
|
||||
},
|
||||
List: ansi.StyleList{
|
||||
LevelIndent: defaultListIndent,
|
||||
},
|
||||
Heading: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
BlockSuffix: "\n",
|
||||
Color: stringPtr(colors.LightBlue),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H1: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: " ",
|
||||
Suffix: " ",
|
||||
Color: stringPtr(colors.Black),
|
||||
BackgroundColor: stringPtr(colors.LightBlue),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H2: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "## ",
|
||||
},
|
||||
},
|
||||
H3: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "### ",
|
||||
},
|
||||
},
|
||||
H4: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "#### ",
|
||||
},
|
||||
},
|
||||
H5: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "##### ",
|
||||
},
|
||||
},
|
||||
H6: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "###### ",
|
||||
Bold: boolPtr(false),
|
||||
},
|
||||
},
|
||||
Strikethrough: ansi.StylePrimitive{
|
||||
CrossedOut: boolPtr(true),
|
||||
},
|
||||
Emph: ansi.StylePrimitive{
|
||||
Italic: boolPtr(true),
|
||||
},
|
||||
Strong: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Orange),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
HorizontalRule: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Gray),
|
||||
Format: "\n--------\n",
|
||||
},
|
||||
Item: ansi.StylePrimitive{
|
||||
BlockPrefix: " ",
|
||||
},
|
||||
Enumeration: ansi.StylePrimitive{
|
||||
BlockPrefix: ". ",
|
||||
},
|
||||
Task: ansi.StyleTask{
|
||||
StylePrimitive: ansi.StylePrimitive{},
|
||||
Ticked: "[✓] ",
|
||||
Unticked: "[ ] ",
|
||||
},
|
||||
Link: ansi.StylePrimitive{
|
||||
|
||||
// Format: "Bozo",
|
||||
},
|
||||
LinkText: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Gold),
|
||||
Bold: boolPtr(true),
|
||||
Conceal: boolPtr(true),
|
||||
Faint: boolPtr(true),
|
||||
},
|
||||
Image: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Purple),
|
||||
Underline: boolPtr(true),
|
||||
},
|
||||
ImageText: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.LightGray),
|
||||
Format: "Image: {{.text}} →",
|
||||
},
|
||||
Code: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: " ",
|
||||
Suffix: " ",
|
||||
Color: stringPtr(colors.Purple),
|
||||
BackgroundColor: stringPtr(colors.BackgroundHighlight),
|
||||
},
|
||||
},
|
||||
CodeBlock: ansi.StyleCodeBlock{
|
||||
StyleBlock: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.LightGray),
|
||||
},
|
||||
Margin: uintPtr(defaultMargin),
|
||||
},
|
||||
Chroma: &ansi.Chroma{
|
||||
Text: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.White),
|
||||
},
|
||||
Error: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.White),
|
||||
BackgroundColor: stringPtr(colors.Red),
|
||||
},
|
||||
Comment: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Gray),
|
||||
},
|
||||
CommentPreproc: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Orange),
|
||||
},
|
||||
Keyword: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
KeywordReserved: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
KeywordNamespace: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
KeywordType: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Turquoise),
|
||||
},
|
||||
Operator: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
Punctuation: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.White),
|
||||
},
|
||||
Name: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Purple),
|
||||
},
|
||||
NameBuiltin: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
NameTag: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Purple),
|
||||
},
|
||||
NameAttribute: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.LightBlue),
|
||||
},
|
||||
NameClass: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.White),
|
||||
Underline: boolPtr(true),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
NameDecorator: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Gold),
|
||||
},
|
||||
NameFunction: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.LightBlue),
|
||||
},
|
||||
LiteralNumber: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Purple),
|
||||
},
|
||||
LiteralString: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Orange),
|
||||
},
|
||||
LiteralStringEscape: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Purple),
|
||||
},
|
||||
GenericDeleted: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Red),
|
||||
},
|
||||
GenericEmph: ansi.StylePrimitive{
|
||||
Italic: boolPtr(true),
|
||||
},
|
||||
GenericInserted: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.Turquoise),
|
||||
},
|
||||
GenericStrong: ansi.StylePrimitive{
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
GenericSubheading: ansi.StylePrimitive{
|
||||
Color: stringPtr(colors.LightGray),
|
||||
},
|
||||
Background: ansi.StylePrimitive{
|
||||
BackgroundColor: stringPtr(colors.BackgroundDim),
|
||||
},
|
||||
},
|
||||
},
|
||||
Table: ansi.StyleTable{
|
||||
StyleBlock: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{},
|
||||
},
|
||||
},
|
||||
DefinitionDescription: ansi.StylePrimitive{
|
||||
BlockPrefix: "\n🠶 ",
|
||||
},
|
||||
}
|
||||
|
||||
return mdStyle
|
||||
}
|
||||
|
||||
const (
|
||||
defaultListIndent = 2
|
||||
defaultListLevelIndent = 4
|
||||
defaultMargin = 0
|
||||
)
|
||||
|
||||
func stringPtr(c lipgloss.Color) *string { return (*string)(&c) }
|
||||
func boolPtr(b bool) *bool { return &b }
|
||||
func uintPtr(u uint) *uint { return &u }
|
Reference in New Issue
Block a user