mirror of
https://github.com/maaslalani/slides.git
synced 2026-01-08 22:07:59 -05:00
doc: add comments to top of public functions and variables
This commit is contained in:
@@ -1,89 +1,91 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/maaslalani/slides/internal/model"
|
"github.com/maaslalani/slides/internal/model"
|
||||||
"github.com/maaslalani/slides/internal/navigation"
|
"github.com/maaslalani/slides/internal/navigation"
|
||||||
"github.com/maaslalani/slides/internal/server"
|
"github.com/maaslalani/slides/internal/server"
|
||||||
"github.com/muesli/coral"
|
"github.com/muesli/coral"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
keyPath string
|
keyPath string
|
||||||
err error
|
err error
|
||||||
fileName string
|
fileName string
|
||||||
|
|
||||||
ServeCmd = &coral.Command{
|
|
||||||
Use: "serve <file.md>",
|
|
||||||
Aliases: []string{"server"},
|
|
||||||
Short: "Start an SSH server to run slides",
|
|
||||||
Args: coral.ArbitraryArgs,
|
|
||||||
RunE: func(cmd *coral.Command, args []string) error {
|
|
||||||
k := os.Getenv("SLIDES_SERVER_KEY_PATH")
|
|
||||||
if k != "" {
|
|
||||||
keyPath = k
|
|
||||||
}
|
|
||||||
h := os.Getenv("SLIDES_SERVER_HOST")
|
|
||||||
if h != "" {
|
|
||||||
host = h
|
|
||||||
}
|
|
||||||
p := os.Getenv("SLIDES_SERVER_PORT")
|
|
||||||
if p != "" {
|
|
||||||
port, _ = strconv.Atoi(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) > 0 {
|
|
||||||
fileName = args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
presentation := model.Model{
|
|
||||||
Page: 0,
|
|
||||||
Date: time.Now().Format("2006-01-02"),
|
|
||||||
FileName: fileName,
|
|
||||||
Search: navigation.NewSearch(),
|
|
||||||
}
|
|
||||||
err = presentation.Load()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := server.NewServer(keyPath, host, port, presentation)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
done := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
log.Printf("Starting Slides server on %s:%d", host, port)
|
|
||||||
go func() {
|
|
||||||
if err = s.Start(); err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-done
|
|
||||||
log.Print("Stopping Slides server")
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer func() { cancel() }()
|
|
||||||
if err := s.Shutdown(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// ServeCmd is the command for serving the presentation. It starts the slides
|
||||||
ServeCmd.Flags().StringVar(&keyPath, "keyPath", "slides", "Server private key path")
|
// server allowing for connections.
|
||||||
ServeCmd.Flags().StringVar(&host, "host", "localhost", "Server host to bind to")
|
var ServeCmd = &coral.Command{
|
||||||
ServeCmd.Flags().IntVar(&port, "port", 53531, "Server port to bind to")
|
Use: "serve <file.md>",
|
||||||
|
Aliases: []string{"server"},
|
||||||
|
Short: "Start an SSH server to run slides",
|
||||||
|
Args: coral.ArbitraryArgs,
|
||||||
|
RunE: func(cmd *coral.Command, args []string) error {
|
||||||
|
k := os.Getenv("SLIDES_SERVER_KEY_PATH")
|
||||||
|
if k != "" {
|
||||||
|
keyPath = k
|
||||||
|
}
|
||||||
|
h := os.Getenv("SLIDES_SERVER_HOST")
|
||||||
|
if h != "" {
|
||||||
|
host = h
|
||||||
|
}
|
||||||
|
p := os.Getenv("SLIDES_SERVER_PORT")
|
||||||
|
if p != "" {
|
||||||
|
port, _ = strconv.Atoi(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
fileName = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation := model.Model{
|
||||||
|
Page: 0,
|
||||||
|
Date: time.Now().Format("2006-01-02"),
|
||||||
|
FileName: fileName,
|
||||||
|
Search: navigation.NewSearch(),
|
||||||
|
}
|
||||||
|
err = presentation.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := server.NewServer(keyPath, host, port, presentation)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
log.Printf("Starting Slides server on %s:%d", host, port)
|
||||||
|
go func() {
|
||||||
|
if err = s.Start(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-done
|
||||||
|
log.Print("Stopping Slides server")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer func() { cancel() }()
|
||||||
|
if err := s.Shutdown(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ServeCmd.Flags().StringVar(&keyPath, "keyPath", "slides", "Server private key path")
|
||||||
|
ServeCmd.Flags().StringVar(&host, "host", "localhost", "Server host to bind to")
|
||||||
|
ServeCmd.Flags().IntVar(&port, "port", 53531, "Server port to bind to")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Block represents a code block.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
Code string
|
Code string
|
||||||
Language string
|
Language string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Result represents the output for an executed code block.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Out string
|
Out string
|
||||||
ExitCode int
|
ExitCode int
|
||||||
@@ -26,6 +28,9 @@ type Result struct {
|
|||||||
var re = regexp.MustCompile("(?s)(?:```|~~~)(\\w+)\n(.*?)\n(?:```|~~~)\\s?")
|
var re = regexp.MustCompile("(?s)(?:```|~~~)(\\w+)\n(.*?)\n(?:```|~~~)\\s?")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// ErrParse is the returned error when we cannot parse the code block (i.e.
|
||||||
|
// there is no code block on the current slide) or the code block is
|
||||||
|
// incorrectly written.
|
||||||
ErrParse = errors.New("Error: could not parse code block")
|
ErrParse = errors.New("Error: could not parse code block")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ package code
|
|||||||
// Placeholders <file>, <name> and <path> can be used.
|
// Placeholders <file>, <name> and <path> can be used.
|
||||||
type cmds [][]string
|
type cmds [][]string
|
||||||
|
|
||||||
// ----
|
// Language represents a programming language with it Extension and Commands to
|
||||||
|
// execute its programs.
|
||||||
type Language struct {
|
type Language struct {
|
||||||
|
// Extension represents the file extension used by this language.
|
||||||
Extension string
|
Extension string
|
||||||
// Commands [][]string // placeholders: <name> file name (without extension),
|
// Commands [][]string // placeholders: <name> file name (without
|
||||||
// <file> file name, <path> path without file name
|
// extension), <file> file name, <path> path without file name
|
||||||
Commands cmds
|
Commands cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@ const (
|
|||||||
Rust = "rust"
|
Rust = "rust"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Languages is a map of supported languages with their extensions and commands
|
||||||
|
// to run to execute the program.
|
||||||
var Languages = map[string]Language{
|
var Languages = map[string]Language{
|
||||||
Bash: {
|
Bash: {
|
||||||
Extension: "sh",
|
Extension: "sh",
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ const (
|
|||||||
delimiter = "\n---\n"
|
delimiter = "\n---\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Model represents the model of this presentation, which contains all the
|
||||||
|
// state related to the current slides.
|
||||||
type Model struct {
|
type Model struct {
|
||||||
Slides []string
|
Slides []string
|
||||||
Page int
|
Page int
|
||||||
@@ -53,6 +55,8 @@ type fileWatchMsg struct{}
|
|||||||
|
|
||||||
var fileInfo os.FileInfo
|
var fileInfo os.FileInfo
|
||||||
|
|
||||||
|
// Init initializes the model and begins watching the slides file for changes
|
||||||
|
// if it exists.
|
||||||
func (m Model) Init() tea.Cmd {
|
func (m Model) Init() tea.Cmd {
|
||||||
if m.FileName == "" {
|
if m.FileName == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -67,6 +71,7 @@ func fileWatchCmd() tea.Cmd {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load loads all of the content and metadata for the presentation.
|
||||||
func (m *Model) Load() error {
|
func (m *Model) Load() error {
|
||||||
var content string
|
var content string
|
||||||
var err error
|
var err error
|
||||||
@@ -194,6 +199,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View renders the current slide in the presentation and the status bar which
|
||||||
|
// contains the author, date, and pagination information.
|
||||||
func (m Model) View() string {
|
func (m Model) View() string {
|
||||||
r, _ := glamour.NewTermRenderer(m.Theme, glamour.WithWordWrap(m.viewport.Width))
|
r, _ := glamour.NewTermRenderer(m.Theme, glamour.WithWordWrap(m.viewport.Width))
|
||||||
slide := m.Slides[m.Page]
|
slide := m.Slides[m.Page]
|
||||||
@@ -284,10 +291,12 @@ func readStdin() (string, error) {
|
|||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentPage returns the current page the presentation is on.
|
||||||
func (m *Model) CurrentPage() int {
|
func (m *Model) CurrentPage() int {
|
||||||
return m.Page
|
return m.Page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPage sets which page the presentation should render.
|
||||||
func (m *Model) SetPage(page int) {
|
func (m *Model) SetPage(page int) {
|
||||||
if m.Page == page {
|
if m.Page == page {
|
||||||
return
|
return
|
||||||
@@ -297,6 +306,7 @@ func (m *Model) SetPage(page int) {
|
|||||||
m.Page = page
|
m.Page = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pages returns all the slides in the presentation.
|
||||||
func (m *Model) Pages() []string {
|
func (m *Model) Pages() []string {
|
||||||
return m.Slides
|
return m.Slides
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func navigateNext(state State) int {
|
|||||||
|
|
||||||
func navigateSlide(buffer string, totalSlides int) int {
|
func navigateSlide(buffer string, totalSlides int) int {
|
||||||
destinationSlide, _ := strconv.Atoi(buffer)
|
destinationSlide, _ := strconv.Atoi(buffer)
|
||||||
destinationSlide -= 1
|
destinationSlide--
|
||||||
|
|
||||||
if destinationSlide > totalSlides-1 {
|
if destinationSlide > totalSlides-1 {
|
||||||
return totalSlides - 1
|
return totalSlides - 1
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Search struct {
|
|||||||
SearchTextInput textinput.Model
|
SearchTextInput textinput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSearch creates and returns a new search model with the default settings.
|
||||||
func NewSearch() Search {
|
func NewSearch() Search {
|
||||||
ti := textinput.NewModel()
|
ti := textinput.NewModel()
|
||||||
ti.Placeholder = "search"
|
ti.Placeholder = "search"
|
||||||
@@ -33,18 +34,19 @@ func NewSearch() Search {
|
|||||||
return Search{SearchTextInput: ti}
|
return Search{SearchTextInput: ti}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query returns the text input's value.
|
||||||
func (s *Search) Query() string {
|
func (s *Search) Query() string {
|
||||||
return s.SearchTextInput.Value()
|
return s.SearchTextInput.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetQuery sets the text input's value
|
||||||
func (s *Search) SetQuery(query string) {
|
func (s *Search) SetQuery(query string) {
|
||||||
s.SearchTextInput.SetValue(query)
|
s.SearchTextInput.SetValue(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark Search as
|
// Done marks the search as done, but does not delete the search buffer. This
|
||||||
// Done - Do not delete search buffer
|
// is useful if, for example, you want to jump to the next result and you
|
||||||
// This is useful if, for example, you want to jump to the next result
|
// therefore still need the buffer.
|
||||||
// and you therefore still need the buffer
|
|
||||||
func (s *Search) Done() {
|
func (s *Search) Done() {
|
||||||
s.Active = false
|
s.Active = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type Block struct {
|
|||||||
Raw string
|
Raw string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String implements the Stringer interface.
|
||||||
func (b Block) String() string {
|
func (b Block) String() string {
|
||||||
return fmt.Sprintf("===\n%s\n%s\n%s\n===", b.Raw, b.Command, b.Input)
|
return fmt.Sprintf("===\n%s\n%s\n%s\n===", b.Raw, b.Command, b.Input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,3 @@ func slidesMiddleware(srv *Server) wish.Middleware {
|
|||||||
}
|
}
|
||||||
return bm.MiddlewareWithProgramHandler(teaHandler, termenv.ANSI256)
|
return bm.MiddlewareWithProgramHandler(teaHandler, termenv.ANSI256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,19 @@ import (
|
|||||||
"github.com/maaslalani/slides/internal/model"
|
"github.com/maaslalani/slides/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Server is the server for hosting this presentation.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
srv *ssh.Server
|
srv *ssh.Server
|
||||||
presentation model.Model
|
presentation model.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new server.
|
// NewServer creates a new server.
|
||||||
func NewServer(keyPath, host string, port int, presentation model.Model) (*Server, error) {
|
func NewServer(keyPath, host string, port int, presentation model.Model) (*Server, error) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
presentation: presentation,
|
presentation: presentation,
|
||||||
}
|
}
|
||||||
srv, err := wish.NewServer(
|
srv, err := wish.NewServer(
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -16,7 +16,7 @@ var (
|
|||||||
rootCmd = &coral.Command{
|
rootCmd = &coral.Command{
|
||||||
Use: "slides <file.md>",
|
Use: "slides <file.md>",
|
||||||
Short: "Terminal based presentation tool",
|
Short: "Terminal based presentation tool",
|
||||||
Args: coral.ArbitraryArgs,
|
Args: coral.ArbitraryArgs,
|
||||||
RunE: func(cmd *coral.Command, args []string) error {
|
RunE: func(cmd *coral.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
var fileName string
|
var fileName string
|
||||||
|
|||||||
@@ -18,35 +18,41 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// Author is the style for the author text in the bottom-left corner of the
|
||||||
|
// presentation.
|
||||||
Author = lipgloss.NewStyle().Foreground(salmon).Align(lipgloss.Left).MarginLeft(2)
|
Author = lipgloss.NewStyle().Foreground(salmon).Align(lipgloss.Left).MarginLeft(2)
|
||||||
Date = lipgloss.NewStyle().Faint(true).Align(lipgloss.Left).Margin(0, 1)
|
// Date is the style for the date text in the bottom-left corner of the
|
||||||
Page = lipgloss.NewStyle().Foreground(salmon).Align(lipgloss.Right).MarginRight(3)
|
// presentation.
|
||||||
Slide = lipgloss.NewStyle().Padding(1)
|
Date = lipgloss.NewStyle().Faint(true).Align(lipgloss.Left).Margin(0, 1)
|
||||||
|
// Page is the style for the pagination progress information text in the
|
||||||
|
// bottom-right corner of the presentation.
|
||||||
|
Page = lipgloss.NewStyle().Foreground(salmon).Align(lipgloss.Right).MarginRight(3)
|
||||||
|
// Slide is the style for the slide.
|
||||||
|
Slide = lipgloss.NewStyle().Padding(1)
|
||||||
|
// Status is the style for the status bar at the bottom of the
|
||||||
|
// presentation.
|
||||||
Status = lipgloss.NewStyle().Padding(1)
|
Status = lipgloss.NewStyle().Padding(1)
|
||||||
|
// Search is the style for the search input at the bottom-left corner of
|
||||||
|
// the screen when searching is active.
|
||||||
Search = lipgloss.NewStyle().Faint(true).Align(lipgloss.Left).MarginLeft(2)
|
Search = lipgloss.NewStyle().Faint(true).Align(lipgloss.Left).MarginLeft(2)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// DefaultTheme is the default theme for the presentation.
|
||||||
//go:embed theme.json
|
//go:embed theme.json
|
||||||
DefaultTheme []byte
|
DefaultTheme []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// JoinHorizontal joins two strings horizontally and fills the space in-between.
|
||||||
func JoinHorizontal(left, right string, width int) string {
|
func JoinHorizontal(left, right string, width int) string {
|
||||||
length := lipgloss.Width(left + right)
|
w := width - lipgloss.Width(right)
|
||||||
if width < length {
|
return lipgloss.PlaceHorizontal(w, lipgloss.Left, left) + right
|
||||||
return left + " " + right
|
|
||||||
}
|
|
||||||
padding := strings.Repeat(" ", width-length)
|
|
||||||
return left + padding + right
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinVertical joins two strings vertically and fills the space in-between.
|
||||||
func JoinVertical(top, bottom string, height int) string {
|
func JoinVertical(top, bottom string, height int) string {
|
||||||
h := lipgloss.Height(top) + lipgloss.Height(bottom)
|
h := height - lipgloss.Height(bottom)
|
||||||
if height < h {
|
return lipgloss.PlaceVertical(h, lipgloss.Top, top) + bottom
|
||||||
return top + "\n" + bottom
|
|
||||||
}
|
|
||||||
fill := strings.Repeat("\n", height-h)
|
|
||||||
return top + fill + bottom
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectTheme picks a glamour style config based
|
// SelectTheme picks a glamour style config based
|
||||||
|
|||||||
Reference in New Issue
Block a user