chore: merge from master

commander-stdout
sammyette 2023-01-20 18:42:44 -04:00
commit e3dca7a996
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
32 changed files with 568 additions and 223 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ docgen
.vim .vim
petals/ petals/
.hugo_build.lock

View File

@ -1,5 +1,19 @@
# 🎀 Changelog # 🎀 Changelog
## Unreleased
### Added
- Documented custom userdata types (Job and Timer Objects)
- Coming with fix is also adding the return types for some functions that were missing it
### Fixed
- `hilbish.which` not working correctly with aliases
## [2.0.1] - 2022-12-28
### Fixed
- Corrected documentation for hooks, removing outdated `command.no-perm`
- Fixed an issue where `cd` with no args would not update the old pwd
- Tiny documentation enhancements for the `hilbish.timer` interface
## [2.0.0] - 2022-12-20 ## [2.0.0] - 2022-12-20
**NOTES FOR USERS/PACKAGERS UPDATING:** **NOTES FOR USERS/PACKAGERS UPDATING:**
- Hilbish now uses [Task] insead of Make for builds. - Hilbish now uses [Task] insead of Make for builds.
@ -611,6 +625,7 @@ This input for example will prompt for more input to complete:
First "stable" release of Hilbish. First "stable" release of Hilbish.
[2.0.1]: https://github.com/Rosettea/Hilbish/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0 [2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0
[2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1 [2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1
[1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0 [1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0

View File

@ -26,52 +26,30 @@ and aims to be infinitely configurable. If something isn't, open an issue!
# Table of Contents # Table of Contents
- [Screenshots](#Screenshots) - [Screenshots](#Screenshots)
- [Installation](#Installation) - [Getting Hilbish](#Getting-Hilbish)
- [Prebuilt Bins](#Prebuilt-binaries)
- [AUR](#AUR)
- [Nixpkgs](#Nixpkgs)
- [Manual Build](#Manual-Build)
- [Contributing](#Contributing) - [Contributing](#Contributing)
# Screenshots # Screenshots
<div align="center"> <div align="center">
<img src="gallery/default.png"><br><br>
<img src="gallery/terminal.png"><br><br> <img src="gallery/terminal.png"><br><br>
<img src="gallery/tab.png"><br><br>
<img src="gallery/pillprompt.png"> <img src="gallery/pillprompt.png">
</div> </div>
# Installation # Getting Hilbish
**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting **NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
from the 2.0 version. It will still be able to compile, but functionality from the 2.0 version. It will still be able to compile, but functionality
may be lacking. may be lacking.
## Prebuilt binaries You can check the [install page](https://rosettea.github.io/Hilbish/install/)
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for on the website for distributed binaries from GitHub or other package repositories.
builds on the master branch. Otherwise, continue reading for steps on compiling.
## AUR ## Prerequisites
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish)
Arch Linux users can install Hilbish from the AUR with the following command:
```sh
yay -S hilbish
```
[![AUR maintainer](https://img.shields.io/aur/maintainer/hilbish?logo=arch-linux&style=flat-square)](https://aur.archlinux.org/packages/hilbish-git)
Or from the latest `master` commit with:
```sh
yay -S hilbish-git
```
## Nixpkgs
Nix/NixOS users can install Hilbish from the central repository, nixpkgs, through the usual ways.
If you're new to nix you should probably read up on how to do that [here](https://nixos.wiki/wiki/Cheatsheet).
## Manual Build
### Prerequisites
- [Go 1.17+](https://go.dev) - [Go 1.17+](https://go.dev)
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**) - [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
### Build ## Build
First, clone Hilbish. The recursive is required, as some Lua libraries First, clone Hilbish. The recursive is required, as some Lua libraries
are submodules. are submodules.
```sh ```sh

View File

@ -92,9 +92,9 @@ func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table {
func _hlalias() {} func _hlalias() {}
// #interface aliases // #interface aliases
// list() -> aliases (table) // list() -> table<string, string>
// Get a table of all aliases, with string keys as the alias and the value as the command. // Get a table of all aliases, with string keys as the alias and the value as the command.
// @returns table<string, string> // --- @returns table<string, string>
func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
aliasesList := rt.NewTable() aliasesList := rt.NewTable()
for k, v := range a.All() { for k, v := range a.All() {

28
api.go
View File

@ -231,8 +231,9 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
} }
// cwd() // cwd() -> string
// Returns the current directory of the shell // Returns the current directory of the shell
// --- @returns string
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
@ -444,12 +445,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// timeout(cb, time) // timeout(cb, time) -> @Timer
// Runs the `cb` function after `time` in milliseconds // Runs the `cb` function after `time` in milliseconds.
// Returns a `timer` object (see `doc timers`). // This creates a timer that starts immediately.
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @returns table // --- @returns Timer
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -470,12 +471,12 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
} }
// interval(cb, time) // interval(cb, time) -> @Timer
// Runs the `cb` function every `time` milliseconds. // Runs the `cb` function every `time` milliseconds.
// Returns a `timer` object (see `doc timers`). // This creates a timer that starts immediately.
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @return table // --- @return Timer
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -536,9 +537,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// which(name) // which(name) -> string
// Checks if `name` is a valid command // Checks if `name` is a valid command.
// Will return the path of the binary, or a basename if it's a commander.
// --- @param name string // --- @param name string
// --- @returns string
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
@ -548,7 +551,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
cmd := aliases.Resolve(name) // itll return either the original command or what was passed
// if name isnt empty its not an issue
alias := aliases.Resolve(name)
cmd := strings.Split(alias, " ")[0]
// check for commander // check for commander
if commands[cmd] != nil { if commands[cmd] != nil {

View File

@ -7,6 +7,7 @@ import (
"go/doc" "go/doc"
"go/parser" "go/parser"
"go/token" "go/token"
"regexp"
"strings" "strings"
"os" "os"
"sync" "sync"
@ -31,6 +32,7 @@ type emmyPiece struct {
} }
type module struct { type module struct {
Types []docPiece
Docs []docPiece Docs []docPiece
Fields []docPiece Fields []docPiece
Properties []docPiece Properties []docPiece
@ -38,6 +40,7 @@ type module struct {
Description string Description string
ParentModule string ParentModule string
HasInterfaces bool HasInterfaces bool
HasTypes bool
} }
type docPiece struct { type docPiece struct {
@ -49,6 +52,7 @@ type docPiece struct {
GoFuncName string GoFuncName string
IsInterface bool IsInterface bool
IsMember bool IsMember bool
IsType bool
Fields []docPiece Fields []docPiece
Properties []docPiece Properties []docPiece
} }
@ -61,6 +65,7 @@ type tag struct {
var docs = make(map[string]module) var docs = make(map[string]module)
var interfaceDocs = make(map[string]module) var interfaceDocs = make(map[string]module)
var emmyDocs = make(map[string][]emmyPiece) var emmyDocs = make(map[string][]emmyPiece)
var typeTable = make(map[string][]string) // [0] = parentMod, [1] = interfaces
var prefix = map[string]string{ var prefix = map[string]string{
"main": "hl", "main": "hl",
"hilbish": "hl", "hilbish": "hl",
@ -119,6 +124,71 @@ func docPieceTag(tagName string, tags map[string][]tag) []docPiece {
return dps return dps
} }
func setupDocType(mod string, typ *doc.Type) *docPiece {
docs := strings.TrimSpace(typ.Doc)
inInterface := strings.HasPrefix(docs, "#interface")
if !inInterface {
return nil
}
tags, doc := getTagsAndDocs(docs)
var interfaces string
typeName := strings.ToUpper(string(typ.Name[0])) + typ.Name[1:]
typeDoc := []string{}
if inInterface {
interfaces = tags["interface"][0].id
}
fields := docPieceTag("field", tags)
properties := docPieceTag("property", tags)
for _, d := range doc {
if strings.HasPrefix(d, "---") {
// TODO: document types in lua
/*
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
emmyLinePieces := strings.Split(emmyLine, " ")
emmyType := emmyLinePieces[0]
if emmyType == "@param" {
em.Params = append(em.Params, emmyLinePieces[1])
}
if emmyType == "@vararg" {
em.Params = append(em.Params, "...") // add vararg
}
em.Annotations = append(em.Annotations, d)
*/
} else {
typeDoc = append(typeDoc, d)
}
}
var isMember bool
if tags["member"] != nil {
isMember = true
}
var parentMod string
if inInterface {
parentMod = mod
}
dps := &docPiece{
Doc: typeDoc,
FuncName: typeName,
Interfacing: interfaces,
IsInterface: inInterface,
IsMember: isMember,
IsType: true,
ParentModule: parentMod,
Fields: fields,
Properties: properties,
}
typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces}
return dps
}
func setupDoc(mod string, fun *doc.Func) *docPiece { func setupDoc(mod string, fun *doc.Func) *docPiece {
docs := strings.TrimSpace(fun.Doc) docs := strings.TrimSpace(fun.Doc)
inInterface := strings.HasPrefix(docs, "#interface") inInterface := strings.HasPrefix(docs, "#interface")
@ -220,6 +290,7 @@ func main() {
for l, f := range pkgs { for l, f := range pkgs {
p := doc.New(f, "./", doc.AllDecls) p := doc.New(f, "./", doc.AllDecls)
pieces := []docPiece{} pieces := []docPiece{}
typePieces := []docPiece{}
mod := l mod := l
if mod == "main" { if mod == "main" {
mod = "hilbish" mod = "hilbish"
@ -237,6 +308,14 @@ func main() {
} }
} }
for _, t := range p.Types { for _, t := range p.Types {
typePiece := setupDocType(mod, t)
if typePiece != nil {
typePieces = append(typePieces, *typePiece)
if typePiece.IsInterface {
hasInterfaces = true
}
}
for _, m := range t.Methods { for _, m := range t.Methods {
piece := setupDoc(mod, m) piece := setupDoc(mod, m)
if piece == nil { if piece == nil {
@ -254,6 +333,7 @@ func main() {
shortDesc := descParts[0] shortDesc := descParts[0]
desc := descParts[1:] desc := descParts[1:]
filteredPieces := []docPiece{} filteredPieces := []docPiece{}
filteredTypePieces := []docPiece{}
for _, piece := range pieces { for _, piece := range pieces {
if !piece.IsInterface { if !piece.IsInterface {
filteredPieces = append(filteredPieces, piece) filteredPieces = append(filteredPieces, piece)
@ -276,10 +356,28 @@ func main() {
interfaceModules[modname].Properties = piece.Properties interfaceModules[modname].Properties = piece.Properties
continue continue
} }
interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece) interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece)
} }
for _, piece := range typePieces {
if !piece.IsInterface {
filteredTypePieces = append(filteredTypePieces, piece)
continue
}
modname := piece.ParentModule + "." + piece.Interfacing
if interfaceModules[modname] == nil {
interfaceModules[modname] = &module{
ParentModule: piece.ParentModule,
}
}
interfaceModules[modname].Types = append(interfaceModules[modname].Types, piece)
}
docs[mod] = module{ docs[mod] = module{
Types: filteredTypePieces,
Docs: filteredPieces, Docs: filteredPieces,
ShortDescription: shortDesc, ShortDescription: shortDesc,
Description: strings.Join(desc, "\n"), Description: strings.Join(desc, "\n"),
@ -335,11 +433,21 @@ func main() {
} }
f.WriteString("\n") f.WriteString("\n")
} }
if len(modu.Docs) != 0 { if len(modu.Docs) != 0 {
typeTag, _ := regexp.Compile(`@\w+`)
f.WriteString("## Functions\n") f.WriteString("## Functions\n")
}
for _, dps := range modu.Docs { for _, dps := range modu.Docs {
f.WriteString(fmt.Sprintf("### %s\n", dps.FuncSig)) if dps.IsMember {
continue
}
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
typName := typ[1:]
typLookup := typeTable[strings.ToLower(typName)]
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
})
f.WriteString(fmt.Sprintf("### %s\n", htmlSig))
for _, doc := range dps.Doc { for _, doc := range dps.Doc {
if !strings.HasPrefix(doc, "---") { if !strings.HasPrefix(doc, "---") {
f.WriteString(doc + "\n") f.WriteString(doc + "\n")
@ -347,6 +455,50 @@ func main() {
} }
f.WriteString("\n") f.WriteString("\n")
} }
}
if len(modu.Types) != 0 {
f.WriteString("## Types\n")
for _, dps := range modu.Types {
f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
for _, doc := range dps.Doc {
if !strings.HasPrefix(doc, "---") {
f.WriteString(doc + "\n")
}
}
if len(dps.Properties) != 0 {
f.WriteString("### Properties\n")
for _, dps := range dps.Properties {
f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
f.WriteString(strings.Join(dps.Doc, " "))
f.WriteString("\n")
}
}
f.WriteString("\n")
typeTag, _ := regexp.Compile(`@\w+`)
f.WriteString("### Methods\n")
for _, dps := range modu.Docs {
if !dps.IsMember {
continue
}
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
// todo: get type from global table to link to
// other pages (hilbish page can link to hilbish.jobs#Job)
typName := typ[1:]
linkedTyp := strings.ToLower(typName) // TODO: link
return fmt.Sprintf(`<a href="#%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
})
f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
for _, doc := range dps.Doc {
if !strings.HasPrefix(doc, "---") {
f.WriteString(doc + "\n")
}
}
f.WriteString("\n")
}
}
}
}(mod, docPath, v) }(mod, docPath, v)
go func(md, modname string, modu module) { go func(md, modname string, modu module) {

View File

@ -35,7 +35,7 @@ replacing <cmd> with the name of the command (for example `command.git`).
`cb` must be a function that returns a table of "completion groups." `cb` must be a function that returns a table of "completion groups."
Check `doc completions` for more information. Check `doc completions` for more information.
### cwd() ### cwd() -> string
Returns the current directory of the shell Returns the current directory of the shell
### exec(cmd) ### exec(cmd)
@ -60,9 +60,9 @@ override this function with your custom handler.
### inputMode(mode) ### inputMode(mode)
Sets the input mode for Hilbish's line reader. Accepts either emacs or vim Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
### interval(cb, time) ### interval(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Runs the `cb` function every `time` milliseconds. Runs the `cb` function every `time` milliseconds.
Returns a `timer` object (see `doc timers`). This creates a timer that starts immediately.
### multiprompt(str) ### multiprompt(str)
Changes the continued line prompt to `str` Changes the continued line prompt to `str`
@ -95,10 +95,11 @@ Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua)
sh, and lua. It also accepts a function, to which if it is passed one sh, and lua. It also accepts a function, to which if it is passed one
will call it to execute user input instead. will call it to execute user input instead.
### timeout(cb, time) ### timeout(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Runs the `cb` function after `time` in milliseconds Runs the `cb` function after `time` in milliseconds.
Returns a `timer` object (see `doc timers`). This creates a timer that starts immediately.
### which(name) ### which(name) -> string
Checks if `name` is a valid command Checks if `name` is a valid command.
Will return the path of the binary, or a basename if it's a commander.

View File

@ -17,9 +17,8 @@ This is an alias (ha) for the `hilbish.alias` function.
### delete(name) ### delete(name)
Removes an alias. Removes an alias.
### list() -> aliases (table) ### list() -> table\<string, string>
Get a table of all aliases, with string keys as the alias and the value as the command. Get a table of all aliases, with string keys as the alias and the value as the command.
@returns table<string, string>
### resolve(alias) -> command (string) ### resolve(alias) -> command (string)
Tries to resolve an alias to its command. Tries to resolve an alias to its command.

View File

@ -14,7 +14,26 @@ Manage interactive jobs in Hilbish via Lua.
Jobs are the name of background tasks/commands. A job can be started via Jobs are the name of background tasks/commands. A job can be started via
interactive usage or with the functions defined below for use in external runners. interactive usage or with the functions defined below for use in external runners.
## Object properties ## Functions
### add(cmdstr, args, execPath)
Adds a new job to the job table. Note that this does not immediately run it.
### all() -> table\<<a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>>
Returns a table of all job objects.
### disown(id)
Disowns a job. This deletes it from the job table.
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
Get a job object via its ID.
### last() -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
Returns the last added job from the table.
## Types
## Job
The Job type describes a Hilbish job.
### Properties
- `cmd`: The user entered command string for the job. - `cmd`: The user entered command string for the job.
- `running`: Whether the job is running or not. - `running`: Whether the job is running or not.
- `id`: The ID of the job in the job table - `id`: The ID of the job in the job table
@ -23,32 +42,17 @@ interactive usage or with the functions defined below for use in external runner
- `stdout`: The standard output of the job. This just means the normal logs of the process. - `stdout`: The standard output of the job. This just means the normal logs of the process.
- `stderr`: The standard error stream of the process. This (usually) includes error messages of the job. - `stderr`: The standard error stream of the process. This (usually) includes error messages of the job.
## Functions ### Methods
### background() #### background()
Puts a job in the background. This acts the same as initially running a job. Puts a job in the background. This acts the same as initially running a job.
### foreground() #### foreground()
Puts a job in the foreground. This will cause it to run like it was Puts a job in the foreground. This will cause it to run like it was
executed normally and wait for it to complete. executed normally and wait for it to complete.
### start() #### start()
Starts running the job. Starts running the job.
### stop() #### stop()
Stops the job from running. Stops the job from running.
### add(cmdstr, args, execPath)
Adds a new job to the job table. Note that this does not immediately run it.
### all() -> jobs (table<Job/Table>)
Returns a table of all job objects.
### disown(id)
Disowns a job. This deletes it from the job table.
### get(id) -> job (Job/Table)
Get a job object via its ID.
### last() -> job (Job/Table)
Returns the last added job from the table.

View File

@ -8,30 +8,52 @@ menu:
--- ---
## Introduction ## Introduction
The timers interface si one to easily set timeouts and intervals
to run functions after a certain time or repeatedly without using If you ever want to run a piece of code on a timed interval, or want to wait
odd tricks. a few seconds, you don't have to rely on timing tricks, as Hilbish has a
timer API to set intervals and timeouts.
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
accessible with `doc hilbish`). But if you want slightly more control over
them, there is the `hilbish.timers` interface. It allows you to get
a timer via ID and control them.
All functions documented with the `Timer` type refer to a Timer object.
An example of usage:
```
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
print 'hello!'
end)
t:start()
print(t.running) // true
```
## Interface fields ## Interface fields
- `INTERVAL`: Constant for an interval timer type - `INTERVAL`: Constant for an interval timer type
- `TIMEOUT`: Constant for a timeout timer type - `TIMEOUT`: Constant for a timeout timer type
## Object properties ## Functions
### create(type, time, callback) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Creates a timer that runs based on the specified `time` in milliseconds.
The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
Retrieves a timer via its ID.
## Types
## Timer
The Job type describes a Hilbish timer.
### Properties
- `type`: What type of timer it is - `type`: What type of timer it is
- `running`: If the timer is running - `running`: If the timer is running
- `duration`: The duration in milliseconds that the timer will run - `duration`: The duration in milliseconds that the timer will run
## Functions ### Methods
### start() #### start()
Starts a timer. Starts a timer.
### stop() #### stop()
Stops a timer. Stops a timer.
### create(type, time, callback)
Creates a timer that runs based on the specified `time` in milliseconds.
The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
### get(id) -> timer (Timer/Table)
Retrieves a timer via its ID.

View File

@ -1 +0,0 @@
hello!

View File

@ -3,5 +3,5 @@
+ `command.not-found` -> cmdStr > Thrown when a command is not found. + `command.not-found` -> cmdStr > Thrown when a command is not found.
+ `command.no-perm` -> cmdStr > Thrown when Hilbish attempts to execute a file but + `command.not-executable` -> cmdStr > Thrown when Hilbish attempts to run a file
has no permission. that is not executable.

View File

@ -1,38 +1 @@
If you ever want to run a piece of code on a timed interval, or want to wait This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
timer API to set intervals and timeouts.
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
accessible with `doc hilbish`). But if you want slightly more control over
them, there is the `hilbish.timers` interface. It allows you to get
a timer via ID.
# Timer Interface
## Functions
- `get(id)` -> timer: get a timer via its id
- `create(type, ms, callback)` -> timer: creates a timer, adding it to the timer pool.
`type` is the type of timer it will be. 0 is an interval, 1 is a timeout.
`ms` is the time it will run for in seconds. callback is the function called
when the timer is triggered.
# Timer Object
All those previously mentioned functions return a `timer` object, to which
you can stop and start a timer again.
An example of usage:
local t = hilbish.timers.create(1, 5000, function()
print 'hello!'
end)
t:stop()
print(t.running, t.duration, t.type)
t:start()
## Properties
- `duration`: amount of time the timer runs for in milliseconds
- `running`: whether the timer is running or not
- `type`: the type of timer (0 is interval, 1 is timeout)
## Functions
- `stop()`: stops the timer. returns an error if it's already stopped
- `start()`: starts the timer. returns an error if it's already started

View File

@ -63,6 +63,7 @@ function hilbish.appendPath(dir) end
function hilbish.complete(scope, cb) end function hilbish.complete(scope, cb) end
--- Returns the current directory of the shell --- Returns the current directory of the shell
--- @returns string
function hilbish.cwd() end function hilbish.cwd() end
--- Replaces running hilbish with `cmd` --- Replaces running hilbish with `cmd`
@ -94,10 +95,10 @@ function hilbish.hinter(line, pos) end
function hilbish.inputMode(mode) end function hilbish.inputMode(mode) end
--- Runs the `cb` function every `time` milliseconds. --- Runs the `cb` function every `time` milliseconds.
--- Returns a `timer` object (see `doc timers`). --- This creates a timer that starts immediately.
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @return table --- @return Timer
function hilbish.interval(cb, time) end function hilbish.interval(cb, time) end
--- Changes the continued line prompt to `str` --- Changes the continued line prompt to `str`
@ -141,15 +142,17 @@ function hilbish.run(cmd, returnOut) end
--- @param mode string|function --- @param mode string|function
function hilbish.runnerMode(mode) end function hilbish.runnerMode(mode) end
--- Runs the `cb` function after `time` in milliseconds --- Runs the `cb` function after `time` in milliseconds.
--- Returns a `timer` object (see `doc timers`). --- This creates a timer that starts immediately.
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @returns table --- @returns Timer
function hilbish.timeout(cb, time) end function hilbish.timeout(cb, time) end
--- Checks if `name` is a valid command --- Checks if `name` is a valid command.
--- Will return the path of the binary, or a basename if it's a commander.
--- @param name string --- @param name string
--- @returns string
function hilbish.which(name) end function hilbish.which(name) end
--- Puts a job in the background. This acts the same as initially running a job. --- Puts a job in the background. This acts the same as initially running a job.
@ -180,7 +183,7 @@ function hilbish.runner.lua(cmd) end
function hilbish.jobs:start() end function hilbish.jobs:start() end
--- Stops the job from running. --- Stops the job from running.
function hilbish.jobs.stop() end function hilbish.jobs:stop() end
--- Runs a command in Hilbish's shell script interpreter. --- Runs a command in Hilbish's shell script interpreter.
--- This is the equivalent of using `source`. --- This is the equivalent of using `source`.

BIN
gallery/tab.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

23
job.go
View File

@ -18,6 +18,15 @@ import (
var jobs *jobHandler var jobs *jobHandler
var jobMetaKey = rt.StringValue("hshjob") var jobMetaKey = rt.StringValue("hshjob")
// #interface jobs
// #property cmd The user entered command string for the job.
// #property running Whether the job is running or not.
// #property id The ID of the job in the job table
// #property pid The Process ID
// #property exitCode The last exit code of the job.
// #property stdout The standard output of the job. This just means the normal logs of the process.
// #property stderr The standard error stream of the process. This (usually) includes error messages of the job.
// The Job type describes a Hilbish job.
type job struct { type job struct {
cmd string cmd string
running bool running bool
@ -135,6 +144,7 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// #interface jobs // #interface jobs
// #member
// stop() // stop()
// Stops the job from running. // Stops the job from running.
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -293,13 +303,6 @@ func (j *jobHandler) stopAll() {
} }
// #interface jobs // #interface jobs
// #property cmd The user entered command string for the job.
// #property running Whether the job is running or not.
// #property id The ID of the job in the job table
// #property pid The Process ID
// #property exitCode The last exit code of the job.
// #property stdout The standard output of the job. This just means the normal logs of the process.
// #property stderr The standard error stream of the process. This (usually) includes error messages of the job.
// background job management // background job management
/* /*
Manage interactive jobs in Hilbish via Lua. Manage interactive jobs in Hilbish via Lua.
@ -384,7 +387,7 @@ func jobUserData(j *job) *rt.UserData {
} }
// #interface jobs // #interface jobs
// get(id) -> job (Job/Table) // get(id) -> @Job
// Get a job object via its ID. // Get a job object via its ID.
// --- @param id number // --- @param id number
// --- @returns Job // --- @returns Job
@ -444,7 +447,7 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// #interface jobs // #interface jobs
// all() -> jobs (table<Job/Table>) // all() -> table<@Job>
// Returns a table of all job objects. // Returns a table of all job objects.
// --- @returns table<Job> // --- @returns table<Job>
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -481,7 +484,7 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// #interface jobs // #interface jobs
// last() -> job (Job/Table) // last() -> @Job
// Returns the last added job from the table. // Returns the last added job from the table.
// --- @returns Job // --- @returns Job
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {

View File

@ -8,14 +8,14 @@ commander.register('cd', function (args, sinks)
if #args > 1 then if #args > 1 then
sinks.out:writeln("cd: too many arguments") sinks.out:writeln("cd: too many arguments")
return 1 return 1
elseif #args > 0 then end
local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
local path = args[1] and args[1] or hilbish.home
if path == '-' then if path == '-' then
path = dirs.old path = dirs.old
sinks.out:writeln(path) sinks.out:writeln(path)
end end
dirs.setOld(hilbish.cwd()) dirs.setOld(hilbish.cwd())
dirs.push(path) dirs.push(path)
@ -25,11 +25,4 @@ commander.register('cd', function (args, sinks)
return 1 return 1
end end
bait.throw('cd', path) bait.throw('cd', path)
return
end
fs.cd(hilbish.home)
bait.throw('cd', hilbish.home)
dirs.push(hilbish.home)
end) end)

View File

@ -4,12 +4,27 @@ local lunacolors = require 'lunacolors'
commander.register('doc', function(args, sinks) commander.register('doc', function(args, sinks)
local moddocPath = hilbish.dataDir .. '/docs/' local moddocPath = hilbish.dataDir .. '/docs/'
local stat = fs.stat '.git/refs/heads/extended-job-api'
if stat then
-- hilbish git
moddocPath = './docs/'
end
local apidocHeader = [[ local apidocHeader = [[
# %s # %s
{grayBg} {white}{italic}%s {reset} {grayBg} {white}{italic}%s {reset}
]] ]]
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.md', '')))
end)
local doc = [[
Welcome to Hilbish's documentation viewer! Here you can find
documentation for builtin functions and other things related
to Hilbish.
Usage: doc <section> [subdoc]
Available sections: ]] .. table.concat(modules, ', ')
if #args > 0 then if #args > 0 then
local mod = args[1] local mod = args[1]
@ -24,6 +39,9 @@ commander.register('doc', function(args, sinks)
subdocName = '_index' subdocName = '_index'
end end
f = io.open(moddocPath .. subdocName .. '.md', 'rb') f = io.open(moddocPath .. subdocName .. '.md', 'rb')
if not f then
f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb')
end
if not f then if not f then
moddocPath = moddocPath .. subdocName .. '/' moddocPath = moddocPath .. subdocName .. '/'
subdocName = args[3] or '_index' subdocName = args[3] or '_index'
@ -31,11 +49,11 @@ commander.register('doc', function(args, sinks)
end end
if not f then if not f then
sinks.out:writeln('No documentation found for ' .. mod .. '.') sinks.out:writeln('No documentation found for ' .. mod .. '.')
return return 1
end end
end end
funcdocs = f:read '*a':gsub('-([%d]+)', '%1') funcdocs = f:read '*a':gsub('-([%d]+)', '%1')
local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' end) local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' or f ~= 'index.md' end)
local subdocs = table.map(moddocs, function(fname) local subdocs = table.map(moddocs, function(fname)
return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', ''))) return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
end) end)
@ -63,8 +81,12 @@ commander.register('doc', function(args, sinks)
if mod == 'api' then if mod == 'api' then
funcdocs = string.format(apidocHeader, vals.title, vals.description or 'no description.') .. funcdocs funcdocs = string.format(apidocHeader, vals.title, vals.description or 'no description.') .. funcdocs
end end
doc = funcdocs:sub(1, #funcdocs - 1)
f:close()
end
local backtickOccurence = 0 local backtickOccurence = 0
local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
backtickOccurence = backtickOccurence + 1 backtickOccurence = backtickOccurence + 1
if backtickOccurence % 2 == 0 then if backtickOccurence % 2 == 0 then
return '{reset}' return '{reset}'
@ -72,25 +94,7 @@ commander.register('doc', function(args, sinks)
return '{underline}{green}' return '{underline}{green}'
end end
end):gsub('#+.-\n', function(t) end):gsub('#+.-\n', function(t)
return '{bold}{magenta}' .. t .. '{reset}' local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<')
end)) return '{bold}{yellow}' .. signature .. '{reset}'
sinks.out:writeln(formattedFuncs) end)))
f:close()
return
end
local modules = table.map(fs.readdir(moddocPath), function(f)
return lunacolors.underline(lunacolors.blue(string.gsub(f, '.md', '')))
end)
sinks.out:writeln [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin
functions and other things.
Usage: doc <section> [subdoc]
A section is a module or a literal section and a subdoc is a subsection for it.
Available sections: ]]
sinks.out:writeln(table.concat(modules, ', '))
end) end)

View File

@ -67,7 +67,7 @@ do
end end
bait.catch('error', function(event, handler, err) bait.catch('error', function(event, handler, err)
bait.release(event, handler) print(string.format('Encountered an error in %s handler\n%s', event, err:sub(8)))
end) end)
bait.catch('command.not-found', function(cmd) bait.catch('command.not-found', function(cmd)

View File

@ -75,6 +75,12 @@ function hilbish.runner.setCurrent(name)
hilbish.runner.setMode(r.run) hilbish.runner.setMode(r.run)
end end
--- Returns the current runner by name.
--- @returns string
function hilbish.runner.getCurrent()
return currentRunner
end
hilbish.runner.add('hybrid', function(input) hilbish.runner.add('hybrid', function(input)
local cmdStr = hilbish.aliases.resolve(input) local cmdStr = hilbish.aliases.resolve(input)

View File

@ -15,6 +15,11 @@ const (
timerTimeout timerTimeout
) )
// #interface timers
// #property type What type of timer it is
// #property running If the timer is running
// #property duration The duration in milliseconds that the timer will run
// The Job type describes a Hilbish timer.
type timer struct{ type timer struct{
id int id int
typ timerType typ timerType

View File

@ -62,7 +62,7 @@ func (th *timersModule) get(id int) *timer {
} }
// #interface timers // #interface timers
// create(type, time, callback) // create(type, time, callback) -> @Timer
// Creates a timer that runs based on the specified `time` in milliseconds. // Creates a timer that runs based on the specified `time` in milliseconds.
// The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT` // The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
// --- @param type number // --- @param type number
@ -91,7 +91,7 @@ func (th *timersModule) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
// #interface timers // #interface timers
// get(id) -> timer (Timer/Table) // get(id) -> @Timer
// Retrieves a timer via its ID. // Retrieves a timer via its ID.
// --- @param id number // --- @param id number
// --- @returns Timer // --- @returns Timer
@ -115,13 +115,30 @@ func (th *timersModule) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// #interface timers // #interface timers
// #field INTERVAL Constant for an interval timer type // #field INTERVAL Constant for an interval timer type
// #field TIMEOUT Constant for a timeout timer type // #field TIMEOUT Constant for a timeout timer type
// #property type What type of timer it is
// #property running If the timer is running
// #property duration The duration in milliseconds that the timer will run
// timeout and interval API // timeout and interval API
// The timers interface si one to easily set timeouts and intervals /*
// to run functions after a certain time or repeatedly without using If you ever want to run a piece of code on a timed interval, or want to wait
// odd tricks. a few seconds, you don't have to rely on timing tricks, as Hilbish has a
timer API to set intervals and timeouts.
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
accessible with `doc hilbish`). But if you want slightly more control over
them, there is the `hilbish.timers` interface. It allows you to get
a timer via ID and control them.
## Timer Object
All functions documented with the `Timer` type refer to a Timer object.
An example of usage:
```
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
print 'hello!'
end)
t:start()
print(t.running) // true
```
*/
func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table { func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table {
timerMethods := rt.NewTable() timerMethods := rt.NewTable()
timerFuncs := map[string]util.LuaExport{ timerFuncs := map[string]util.LuaExport{

View File

@ -11,7 +11,7 @@ var (
// Version info // Version info
var ( var (
ver = "v2.0.0" ver = "v2.1.0"
releaseName = "Hibiscus" releaseName = "Hibiscus"
gitCommit string gitCommit string
gitBranch string gitBranch string

View File

@ -20,6 +20,16 @@ enableGitInfo = true
name = 'Docs' name = 'Docs'
pageref = '/docs' pageref = '/docs'
weight = 3 weight = 3
[[menu.nav]]
identifier = 'blog'
name = 'Blog'
pageref = '/blog'
weight = 4
[markup.goldmark.renderer] [markup.goldmark.renderer]
unsafe = true unsafe = true
[author]
[author.sammyette]
name = 'sammyette'
picture = 'https://avatars1.githubusercontent.com/u/38820196?s=460&u=b9f4efb2375bae6cb30656d790c6e0a2939327c0&v=4'

View File

@ -0,0 +1,114 @@
---
title: "Hilbish v2.0 Release"
date: 2022-12-29T01:55:21+00:00
---
Hilbish v2.0 has been released!
Well actually, it was released a week ago, but I only wrote this
Hilbish blog *after* that.
This is a **big** release, coming 9 months after the previous v1.2.0 and
featuring over 40+ bug fixes and tons of new features and enhancements, so
let's see what is in this release.
# Documentation
When querying about the problems people have with Hilbish, one of the
issues was its poor documentation. Hilbish had plain text, autogenerated
documentation which only covered the module functions (bait, hilbish,
commander, etc.) and did not include the interfaces (`hilbish.timers`,
`hilbish.jobs` and all that).
I have tried to improve this by working on documenting all the
interfaces (except for some functions of `hilbish.runner`, that's hard to do)
and made the documentation markdown for use on this website. This means
that users can look at documentation here or with the `doc` command.
Hopefully this addresses documentation complaints, and if not, please open an issue.
# Main Bug Fixes
As this is a piece of software with no unit testing that is maintained by me alone,
there is gonna be either some bug or something that I overlooked when
making a change. I make a lot of mistakes. There's also the other fact that
sometimes there's just bugs for any other reasosn. Good thing I fixed
more than 40 of those bugs in this release!
## Readline Bug Fixes
The pure Go readline library is good in some ways and bad in others.
A good portion of the bug fixes are for the readline library, and also
related to text input with east asian characters and the like (Korean, Japanese,
etc.)
A few of the fixes (and additions) include:
- Fixing various crashes, including when there is a "stray" newline at the end of text
- Grid completion menu causing spam and duplicate text when there are items longer than
the terminal and/or contain Japanese or other characters.
- Cursor positioning with CJK characters
- Adding new keybinds and fixing others
## Other fixes
There are a lot more fixes, even more than the ones listed here, but these are the main ones:
- Don't put alias expanded command in history (I've fixed this 5 times now....)
- Handle stdin being nonblocking
- Completion related fixes, like showing the full name, completing files with spaces
# Breaking changes
This release is a major version bump not only because there are tons of fixes, but because
there are breaking changes. This means that there are some changes done which would
cause errors with an old user config (breaking).
## Lua 5.4
The most important is the use of a new Lua VM library. Previously, Hilbish
used gopher-lua, which implements Lua 5.1. This has been changed to
[golua](https://github.com/arnodel/golua/), which implements Lua 5.4.
Moving from 5.1 to 5.4 does have breaking changes even if it doesn't seem like it,
and since these are different Lua implementations, there may be some differences there too.
## Userdata
Previously, objects such as jobs or timers were represented by tables.
This has been changed to userdata to make more sense.
## Other changes
Runner functions are now required to return a table.
It can (at the moment) have 4 variables:
- `input` (user input)
- `exitCode` (exit code)
- `error` (error message)
- `continue` (whether to prompt for more input)
User input has been added to the return to account for runners wanting to
prompt for continued input, and to add it properly to history. `continue`
got added so that it would be easier for runners to get continued input
without having to actually handle it at all.
The MacOS config paths now match Linux, since it makes more sense for
a program like Hilbish.
The Hilbish greeting is now an *opt*, and is printed by default.
# Feature Additions
Besides fixes and changes, this release also includes a good portion of
new features! Users can now add handlers for syntax highlighting and
inline hinting.
Some new hooks have been added, like `hilbish.cancel` and `hilbish.init`.
You can look at all the hooks via the `doc hooks` command
Job management functions have also been added. You can now put jobs in the
foreground/background and disown them via the expected commands and also
via the Lua API.
The `hilbish.timers` API interface was also added in this release!
# Closing Off
Hilbish has gone from something small and simple for myself to a slightly
advanced shell with a decent amount of features, and a few users. It
still hasn't reached levels of other alt shells in regards to literally
everything, but the goal is to get there!
If you want to check the FULL changelog, you can [do so here.](https://github.com/Rosettea/Hilbish/releases/tag/v2.0.0)
This v2.0 release marks an advancement in Hilbish (and also how long
one of my projects hasn't died) and I hope it can advance even further.
Thanks for reading, and I'll be back for the v2.1 release notes, or maybe
something else in between.

View File

@ -0,0 +1,6 @@
---
title: "Welcome to the Hilbish blog"
---
Hello! Welcome to the Hilbish blog. This will mainly contain release
announcements and some other things relating to Hilbish (development).

View File

@ -1,6 +1,11 @@
<h{{ (add .Level 1) }} id="{{ .Anchor | safeURL }}">
<h{{ (add .Level 1) }} id="{{ .Anchor | safeURL }}" class="heading">
{{ .Text | safeHTML }} {{ .Text | safeHTML }}
<a href="#{{ .Anchor | safeURL }}" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h{{ (add .Level 1) }}> </h{{ (add .Level 1) }}>
{{ if eq .Text ""}} {{ if eq .Text ""}}
<hr> <hr>
{{ end }} {{ end }}

View File

@ -1,4 +1 @@
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if eq (substr .Destination 0 4) "http" }} target="_blank" rel="noopener"{{ end }}> <a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if eq (substr .Destination 0 4) "http" }} target="_blank" rel="noopener"{{ end }}>{{ .Text | safeHTML }}</a>
{{ .Text | safeHTML }}
</a>

View File

@ -0,0 +1,21 @@
{{ define "main" }}
<main>
<div class="row row-cols-1 row-cols-md-1 g-4">
{{ range where .Site.RegularPages "Section" "in" "blog" }}
<div class="col d-flex justify-content-center">
<div class="card" style="width: 56rem;">
<div class="card-body">
<a href="{{ .Permalink }}"><h5 class="card-title">{{ .Title }}</h5></a>
<h6 class='card-subtitle text-muted mb-2'>
{{- if isset .Params "date" -}}
<time>{{ .Date.Format "January 2, 2006" }}</time>
{{- end -}}
</h6>
<p class="card-text">{{if .Description}}{{ .Description }}{{ else }}{{ .Summary }}{{ end }}</p>
</div>
</div>
</div>
{{- end }}
</div>
</main>
{{ end }}

View File

@ -2,7 +2,16 @@
<main> <main>
<div class="container mt-2"> <div class="container mt-2">
<h1>{{ .Title }}</h1> <h1>{{ .Title }}</h1>
<img src='{{ .Site.Author.sammyette.picture }}' width=48 style='border-radius: 100%'>
<em class='text-muted'>
by <strong>{{ .Site.Author.sammyette.name }}</strong>
{{- if isset .Params "date" -}}
<time> // {{ .Date.Format "January 2, 2006" }}</time>
{{- end -}}
</em>
<div class='my-4'>
{{.Content}} {{.Content}}
</div> </div>
</div>
</main> </main>
{{ end }} {{ end }}

View File

@ -23,4 +23,16 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
.heading > .heading-link {
opacity: 0
}
.heading:hover > .heading-link {
visibility: visible;
opacity: 1;
transition: all .1s ease-in;
}
</style>
</head> </head>