From 9cfbe0aa0149c306190e43c574164b99a3e2ee48 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 2 May 2023 23:19:43 -0400 Subject: [PATCH] Wintergreen is now a plugin --- default/theme/assets/default.png | Bin 0 -> 300 bytes default/theme/default.go | 123 ++----- plugins/wintergreen/main.go | 21 ++ .../assets/wintergreen-icons-large.png | Bin 0 -> 2949 bytes .../assets/wintergreen-icons-small.png | Bin 0 -> 2715 bytes .../wintergreen}/assets/wintergreen.png | Bin plugins/wintergreen/wintergreen/doc.go | 2 + .../wintergreen/wintergreen/wintergreen.go | 314 ++++++++++++++++++ scripts/install-backends.sh | 10 +- scripts/install-wintergreen.sh | 7 + scripts/plugin.sh | 11 + 11 files changed, 383 insertions(+), 105 deletions(-) create mode 100644 default/theme/assets/default.png create mode 100644 plugins/wintergreen/main.go create mode 100644 plugins/wintergreen/wintergreen/assets/wintergreen-icons-large.png create mode 100644 plugins/wintergreen/wintergreen/assets/wintergreen-icons-small.png rename {default/theme => plugins/wintergreen/wintergreen}/assets/wintergreen.png (100%) create mode 100644 plugins/wintergreen/wintergreen/doc.go create mode 100644 plugins/wintergreen/wintergreen/wintergreen.go create mode 100644 scripts/install-wintergreen.sh create mode 100644 scripts/plugin.sh diff --git a/default/theme/assets/default.png b/default/theme/assets/default.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fae9a3558f2bf76efbdf6261c192f963cca14b GIT binary patch literal 300 zcmeAS@N?(olHy`uVBq!ia0vp^79h;T3?#3PH zr6qmZ8^3Vaw(|?P9@u%SacS^X%}<)yJ|PCq8d^dT_WWPoNVq6>dvGzT-f7Y{`|R4siDg@YL8R)MfReb*A_cGEZqn61cRrmpUXO@geCx=0(G+h literal 0 HcmV?d00001 diff --git a/default/theme/default.go b/default/theme/default.go index 0c0754a..ab07bc4 100644 --- a/default/theme/default.go +++ b/default/theme/default.go @@ -13,10 +13,10 @@ import "git.tebibyte.media/sashakoshka/tomo/artist/artutil" import defaultfont "git.tebibyte.media/sashakoshka/tomo/default/font" import "git.tebibyte.media/sashakoshka/tomo/artist/patterns" -//go:embed assets/wintergreen.png +//go:embed assets/default.png var defaultAtlasBytes []byte var defaultAtlas artist.Canvas -var defaultTextures [17][9]artist.Pattern +var defaultTextures [7][7]artist.Pattern //go:embed assets/wintergreen-icons-small.png var defaultIconsSmallAtlasBytes []byte var defaultIconsSmall [640]binaryIcon @@ -25,7 +25,7 @@ var defaultIconsLargeAtlasBytes []byte var defaultIconsLarge [640]binaryIcon func atlasCell (col, row int, border artist.Inset) { - bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16)) + bounds := image.Rect(0, 0, 8, 8).Add(image.Pt(col, row).Mul(8)) defaultTextures[col][row] = patterns.Border { Canvas: artist.Cut(defaultAtlas, bounds), Inset: border, @@ -87,41 +87,13 @@ func init () { defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes)) defaultAtlas = artist.FromImage(defaultAtlasImage) - // PatternDead - atlasCol(0, artist.Inset { }) - // PatternRaised - atlasCol(1, artist.Inset { 6, 6, 6, 6 }) - // PatternSunken - atlasCol(2, artist.Inset { 4, 4, 4, 4 }) - // PatternPinboard - atlasCol(3, artist.Inset { 2, 2, 2, 2 }) - // PatternButton - atlasCol(4, artist.Inset { 6, 6, 6, 6 }) - // PatternInput - atlasCol(5, artist.Inset { 4, 4, 4, 4 }) - // PatternGutter - atlasCol(6, artist.Inset { 7, 7, 7, 7 }) - // PatternHandle - atlasCol(7, artist.Inset { 3, 3, 3, 3 }) - // PatternLine - atlasCol(8, artist.Inset { 1, 1, 1, 1 }) - // PatternMercury - atlasCol(13, artist.Inset { 2, 2, 2, 2 }) - // PatternTableHead: - atlasCol(14, artist.Inset { 4, 4, 4, 4 }) - // PatternTableCell: - atlasCol(15, artist.Inset { 4, 4, 4, 4 }) - // PatternLamp: - atlasCol(16, artist.Inset { 4, 3, 4, 3 }) - - // PatternButton: basic.checkbox - atlasCol(9, artist.Inset { 3, 3, 3, 3 }) - // PatternRaised: basic.listEntry - atlasCol(10, artist.Inset { 3, 3, 3, 3 }) - // PatternRaised: fun.flatKey - atlasCol(11, artist.Inset { 3, 3, 5, 3 }) - // PatternRaised: fun.sharpKey - atlasCol(12, artist.Inset { 3, 3, 4, 3 }) + atlasCol(0, artist.I(0)) + atlasCol(1, artist.I(3)) + atlasCol(2, artist.I(1)) + atlasCol(3, artist.I(1)) + atlasCol(4, artist.I(1)) + atlasCol(5, artist.I(2)) + atlasCol(6, artist.I(1)) // set up small icons defaultIconsSmallAtlasImage, _, _ := image.Decode ( @@ -217,26 +189,16 @@ func (Default) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) artist.P case tomo.PatternRaised: return defaultTextures[1][offset] case tomo.PatternSunken: return defaultTextures[2][offset] case tomo.PatternPinboard: return defaultTextures[3][offset] - case tomo.PatternButton: - switch { - case c.Match("tomo", "checkbox", ""): - return defaultTextures[9][offset] - case c.Match("tomo", "piano", "flatKey"): - return defaultTextures[11][offset] - case c.Match("tomo", "piano", "sharpKey"): - return defaultTextures[12][offset] - default: - return defaultTextures[4][offset] - } - case tomo.PatternInput: return defaultTextures[5][offset] - case tomo.PatternGutter: return defaultTextures[6][offset] - case tomo.PatternHandle: return defaultTextures[7][offset] - case tomo.PatternLine: return defaultTextures[8][offset] - case tomo.PatternMercury: return defaultTextures[13][offset] - case tomo.PatternTableHead: return defaultTextures[14][offset] - case tomo.PatternTableCell: return defaultTextures[15][offset] - case tomo.PatternLamp: return defaultTextures[16][offset] - default: return patterns.Uhex(0xFF00FFFF) + case tomo.PatternButton: return defaultTextures[1][offset] + case tomo.PatternInput: return defaultTextures[2][offset] + case tomo.PatternGutter: return defaultTextures[2][offset] + case tomo.PatternHandle: return defaultTextures[3][offset] + case tomo.PatternLine: return defaultTextures[0][offset] + case tomo.PatternMercury: return defaultTextures[4][offset] + case tomo.PatternTableHead: return defaultTextures[5][offset] + case tomo.PatternTableCell: return defaultTextures[5][offset] + case tomo.PatternLamp: return defaultTextures[6][offset] + default: return patterns.Uhex(0xFF00FFFF) } } @@ -262,56 +224,25 @@ func (Default) Color (id tomo.Color, state tomo.State, c tomo.Case) color.RGBA { tomo.ColorBrightWhite: 0xcfd7d2FF, tomo.ColorForeground: 0x000000FF, - tomo.ColorMidground: 0x97A09BFF, + tomo.ColorMidground: 0x656565FF, tomo.ColorBackground: 0xAAAAAAFF, - tomo.ColorShadow: 0x445754FF, - tomo.ColorShine: 0xCFD7D2FF, - tomo.ColorAccent: 0x408090FF, + tomo.ColorShadow: 0x000000FF, + tomo.ColorShine: 0xFFFFFFFF, + tomo.ColorAccent: 0xff3300FF, } [id]) } // Padding returns the default padding value for the given pattern. func (Default) Padding (id tomo.Pattern, c tomo.Case) artist.Inset { switch id { - case tomo.PatternSunken: - if c.Match("tomo", "progressBar", "") { - return artist.I(2, 1, 1, 2) - } else if c.Match("tomo", "list", "") { - return artist.I(2) - } else if c.Match("tomo", "flowList", "") { - return artist.I(2) - } else { - return artist.I(8) - } - case tomo.PatternPinboard: - if c.Match("tomo", "piano", "") { - return artist.I(2) - } else { - return artist.I(8) - } - case tomo.PatternTableCell: return artist.I(5) - case tomo.PatternTableHead: return artist.I(5) - case tomo.PatternGutter: return artist.I(0) - case tomo.PatternLine: return artist.I(1) - case tomo.PatternMercury: return artist.I(5) - case tomo.PatternLamp: return artist.I(5, 5, 5, 6) - default: return artist.I(8) + case tomo.PatternGutter: return artist.I(0) + default: return artist.I(6) } } // Margin returns the default margin value for the given pattern. func (Default) Margin (id tomo.Pattern, c tomo.Case) image.Point { - switch id { - case tomo.PatternSunken: - if c.Match("tomo", "list", "") { - return image.Pt(-1, -1) - } else if c.Match("tomo", "flowList", "") { - return image.Pt(-1, -1) - } else { - return image.Pt(8, 8) - } - default: return image.Pt(8, 8) - } + return image.Pt(6, 6) } // Hints returns rendering optimization hints for a particular pattern. diff --git a/plugins/wintergreen/main.go b/plugins/wintergreen/main.go new file mode 100644 index 0000000..6d1bcc4 --- /dev/null +++ b/plugins/wintergreen/main.go @@ -0,0 +1,21 @@ +// Plugin wintergreen provides a calm, bluish green theme. +package main + +import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/plugins/wintergreen/wintergreen" + +func Expects () tomo.Version { + return tomo.Version { 0, 0, 0 } +} + +func Name () string { + return "Wintergreen" +} + +func Description () string { + return "A calm, bluish green theme." +} + +func NewTheme () (tomo.Theme) { + return wintergreen.Theme { } +} diff --git a/plugins/wintergreen/wintergreen/assets/wintergreen-icons-large.png b/plugins/wintergreen/wintergreen/assets/wintergreen-icons-large.png new file mode 100644 index 0000000000000000000000000000000000000000..c4dae369cd3d919813f7872db67084e78bf96a06 GIT binary patch literal 2949 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 V!C`FLTZ8}r002ovPDHLkV1lMt3;zHB literal 0 HcmV?d00001 diff --git a/default/theme/assets/wintergreen.png b/plugins/wintergreen/wintergreen/assets/wintergreen.png similarity index 100% rename from default/theme/assets/wintergreen.png rename to plugins/wintergreen/wintergreen/assets/wintergreen.png diff --git a/plugins/wintergreen/wintergreen/doc.go b/plugins/wintergreen/wintergreen/doc.go new file mode 100644 index 0000000..7ac40d8 --- /dev/null +++ b/plugins/wintergreen/wintergreen/doc.go @@ -0,0 +1,2 @@ +// Package wintergreen contains the wintergreen theme. +package wintergreen diff --git a/plugins/wintergreen/wintergreen/wintergreen.go b/plugins/wintergreen/wintergreen/wintergreen.go new file mode 100644 index 0000000..380a506 --- /dev/null +++ b/plugins/wintergreen/wintergreen/wintergreen.go @@ -0,0 +1,314 @@ +package wintergreen + +import "image" +import "bytes" +import _ "embed" +import _ "image/png" +import "image/color" +import "golang.org/x/image/font" +import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/data" +import "git.tebibyte.media/sashakoshka/tomo/artist" +import "git.tebibyte.media/sashakoshka/tomo/artist/artutil" +import defaultfont "git.tebibyte.media/sashakoshka/tomo/default/font" +import "git.tebibyte.media/sashakoshka/tomo/artist/patterns" + +//go:embed assets/wintergreen.png +var defaultAtlasBytes []byte +var defaultAtlas artist.Canvas +var defaultTextures [17][9]artist.Pattern +//go:embed assets/wintergreen-icons-small.png +var defaultIconsSmallAtlasBytes []byte +var defaultIconsSmall [640]binaryIcon +//go:embed assets/wintergreen-icons-large.png +var defaultIconsLargeAtlasBytes []byte +var defaultIconsLarge [640]binaryIcon + +func atlasCell (col, row int, border artist.Inset) { + bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16)) + defaultTextures[col][row] = patterns.Border { + Canvas: artist.Cut(defaultAtlas, bounds), + Inset: border, + } +} + +func atlasCol (col int, border artist.Inset) { + for index, _ := range defaultTextures[col] { + atlasCell(col, index, border) + } +} + +type binaryIcon struct { + data []bool + stride int +} + +func (icon binaryIcon) Draw (destination artist.Canvas, color color.RGBA, at image.Point) { + bounds := icon.Bounds().Add(at).Intersect(destination.Bounds()) + point := image.Point { } + data, stride := destination.Buffer() + + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { + srcPoint := point.Sub(at) + srcIndex := srcPoint.X + srcPoint.Y * icon.stride + dstIndex := point.X + point.Y * stride + if icon.data[srcIndex] { + data[dstIndex] = color + } + }} +} + +func (icon binaryIcon) Bounds () image.Rectangle { + return image.Rect(0, 0, icon.stride, len(icon.data) / icon.stride) +} + +func binaryIconFrom (source image.Image, clip image.Rectangle) (icon binaryIcon) { + bounds := source.Bounds().Intersect(clip) + if bounds.Empty() { return } + + icon.stride = bounds.Dx() + icon.data = make([]bool, bounds.Dx() * bounds.Dy()) + + point := image.Point { } + dstIndex := 0 + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { + r, g, b, a := source.At(point.X, point.Y).RGBA() + if a > 0x8000 && (r + g + b) / 3 < 0x8000 { + icon.data[dstIndex] = true + } + dstIndex ++ + }} + return +} + +func init () { + defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes)) + defaultAtlas = artist.FromImage(defaultAtlasImage) + + // PatternDead + atlasCol(0, artist.Inset { }) + // PatternRaised + atlasCol(1, artist.Inset { 6, 6, 6, 6 }) + // PatternSunken + atlasCol(2, artist.Inset { 4, 4, 4, 4 }) + // PatternPinboard + atlasCol(3, artist.Inset { 2, 2, 2, 2 }) + // PatternButton + atlasCol(4, artist.Inset { 6, 6, 6, 6 }) + // PatternInput + atlasCol(5, artist.Inset { 4, 4, 4, 4 }) + // PatternGutter + atlasCol(6, artist.Inset { 7, 7, 7, 7 }) + // PatternHandle + atlasCol(7, artist.Inset { 3, 3, 3, 3 }) + // PatternLine + atlasCol(8, artist.Inset { 1, 1, 1, 1 }) + // PatternMercury + atlasCol(13, artist.Inset { 2, 2, 2, 2 }) + // PatternTableHead: + atlasCol(14, artist.Inset { 4, 4, 4, 4 }) + // PatternTableCell: + atlasCol(15, artist.Inset { 4, 4, 4, 4 }) + // PatternLamp: + atlasCol(16, artist.Inset { 4, 3, 4, 3 }) + + // PatternButton: basic.checkbox + atlasCol(9, artist.Inset { 3, 3, 3, 3 }) + // PatternRaised: basic.listEntry + atlasCol(10, artist.Inset { 3, 3, 3, 3 }) + // PatternRaised: fun.flatKey + atlasCol(11, artist.Inset { 3, 3, 5, 3 }) + // PatternRaised: fun.sharpKey + atlasCol(12, artist.Inset { 3, 3, 4, 3 }) + + // set up small icons + defaultIconsSmallAtlasImage, _, _ := image.Decode ( + bytes.NewReader(defaultIconsSmallAtlasBytes)) + point := image.Point { } + iconIndex := 0 + for point.Y = 0; point.Y < 20; point.Y ++ { + for point.X = 0; point.X < 32; point.X ++ { + defaultIconsSmall[iconIndex] = binaryIconFrom ( + defaultIconsSmallAtlasImage, + image.Rect(0, 0, 16, 16).Add(point.Mul(16))) + iconIndex ++ + }} + + // set up large icons + defaultIconsLargeAtlasImage, _, _ := image.Decode ( + bytes.NewReader(defaultIconsLargeAtlasBytes)) + point = image.Point { } + iconIndex = 0 + for point.Y = 0; point.Y < 8; point.Y ++ { + for point.X = 0; point.X < 32; point.X ++ { + defaultIconsLarge[iconIndex] = binaryIconFrom ( + defaultIconsLargeAtlasImage, + image.Rect(0, 0, 32, 32).Add(point.Mul(32))) + iconIndex ++ + }} + iconIndex = 384 + for point.Y = 8; point.Y < 12; point.Y ++ { + for point.X = 0; point.X < 32; point.X ++ { + defaultIconsLarge[iconIndex] = binaryIconFrom ( + defaultIconsLargeAtlasImage, + image.Rect(0, 0, 32, 32).Add(point.Mul(32))) + iconIndex ++ + }} +} + +type Theme struct { } + +func (Theme) FontFace (style tomo.FontStyle, size tomo.FontSize, c tomo.Case) font.Face { + switch style { + case tomo.FontStyleBold: + return defaultfont.FaceBold + case tomo.FontStyleItalic: + return defaultfont.FaceItalic + case tomo.FontStyleBoldItalic: + return defaultfont.FaceBoldItalic + default: + return defaultfont.FaceRegular + } +} + +func (Theme) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon { + if size == tomo.IconSizeLarge { + if id < 0 || int(id) >= len(defaultIconsLarge) { + return nil + } else { + return defaultIconsLarge[id] + } + } else { + if id < 0 || int(id) >= len(defaultIconsSmall) { + return nil + } else { + return defaultIconsSmall[id] + } + } +} + +func (Theme) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) artist.Icon { + // TODO + return nil +} + +func (Theme) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) artist.Pattern { + offset := 0; switch { + case state.Disabled: offset = 1 + case state.Pressed && state.On: offset = 4 + case state.Focused && state.On: offset = 6 + case state.On: offset = 2 + case state.Pressed: offset = 3 + case state.Focused: offset = 5 + } + + switch id { + case tomo.PatternBackground: return patterns.Uhex(0xaaaaaaFF) + case tomo.PatternDead: return defaultTextures[0][offset] + case tomo.PatternRaised: return defaultTextures[1][offset] + case tomo.PatternSunken: return defaultTextures[2][offset] + case tomo.PatternPinboard: return defaultTextures[3][offset] + case tomo.PatternButton: + switch { + case c.Match("tomo", "checkbox", ""): + return defaultTextures[9][offset] + case c.Match("tomo", "piano", "flatKey"): + return defaultTextures[11][offset] + case c.Match("tomo", "piano", "sharpKey"): + return defaultTextures[12][offset] + default: + return defaultTextures[4][offset] + } + case tomo.PatternInput: return defaultTextures[5][offset] + case tomo.PatternGutter: return defaultTextures[6][offset] + case tomo.PatternHandle: return defaultTextures[7][offset] + case tomo.PatternLine: return defaultTextures[8][offset] + case tomo.PatternMercury: return defaultTextures[13][offset] + case tomo.PatternTableHead: return defaultTextures[14][offset] + case tomo.PatternTableCell: return defaultTextures[15][offset] + case tomo.PatternLamp: return defaultTextures[16][offset] + default: return patterns.Uhex(0xFF00FFFF) + } +} + +func (Theme) Color (id tomo.Color, state tomo.State, c tomo.Case) color.RGBA { + if state.Disabled { return artutil.Hex(0x444444FF) } + + return artutil.Hex (map[tomo.Color] uint32 { + tomo.ColorBlack: 0x272d24FF, + tomo.ColorRed: 0x8c4230FF, + tomo.ColorGreen: 0x69905fFF, + tomo.ColorYellow: 0x9a973dFF, + tomo.ColorBlue: 0x3d808fFF, + tomo.ColorPurple: 0x8c608bFF, + tomo.ColorCyan: 0x3d8f84FF, + tomo.ColorWhite: 0xaea894FF, + tomo.ColorBrightBlack: 0x4f5142FF, + tomo.ColorBrightRed: 0xbd6f59FF, + tomo.ColorBrightGreen: 0x8dad84FF, + tomo.ColorBrightYellow: 0xe2c558FF, + tomo.ColorBrightBlue: 0x77b1beFF, + tomo.ColorBrightPurple: 0xc991c8FF, + tomo.ColorBrightCyan: 0x74c7b7FF, + tomo.ColorBrightWhite: 0xcfd7d2FF, + + tomo.ColorForeground: 0x000000FF, + tomo.ColorMidground: 0x97A09BFF, + tomo.ColorBackground: 0xAAAAAAFF, + tomo.ColorShadow: 0x445754FF, + tomo.ColorShine: 0xCFD7D2FF, + tomo.ColorAccent: 0x408090FF, + } [id]) +} + +func (Theme) Padding (id tomo.Pattern, c tomo.Case) artist.Inset { + switch id { + case tomo.PatternSunken: + if c.Match("tomo", "progressBar", "") { + return artist.I(2, 1, 1, 2) + } else if c.Match("tomo", "list", "") { + return artist.I(2) + } else if c.Match("tomo", "flowList", "") { + return artist.I(2) + } else { + return artist.I(8) + } + case tomo.PatternPinboard: + if c.Match("tomo", "piano", "") { + return artist.I(2) + } else { + return artist.I(8) + } + case tomo.PatternTableCell: return artist.I(5) + case tomo.PatternTableHead: return artist.I(5) + case tomo.PatternGutter: return artist.I(0) + case tomo.PatternLine: return artist.I(1) + case tomo.PatternMercury: return artist.I(5) + case tomo.PatternLamp: return artist.I(5, 5, 5, 6) + default: return artist.I(8) + } +} + +func (Theme) Margin (id tomo.Pattern, c tomo.Case) image.Point { + switch id { + case tomo.PatternSunken: + if c.Match("tomo", "list", "") { + return image.Pt(-1, -1) + } else if c.Match("tomo", "flowList", "") { + return image.Pt(-1, -1) + } else { + return image.Pt(8, 8) + } + default: return image.Pt(8, 8) + } +} + +func (Theme) Hints (pattern tomo.Pattern, c tomo.Case) (hints tomo.Hints) { + return +} + +func (Theme) Sink (pattern tomo.Pattern, c tomo.Case) image.Point { + return image.Point { 1, 1 } +} diff --git a/scripts/install-backends.sh b/scripts/install-backends.sh index 00d2187..672caca 100644 --- a/scripts/install-backends.sh +++ b/scripts/install-backends.sh @@ -1,14 +1,6 @@ #!/bin/sh -pluginInstallPath="$HOME/.local/lib/nasin/plugins" - -mkdir -p build -mkdir -p "$pluginInstallPath" - -install() { - go build -buildmode=plugin -o "build/$1.so" "./plugins/$1" && \ - cp build/x.so $pluginInstallPath -} +. scripts/plugin.sh echo "... installing X backend" install x diff --git a/scripts/install-wintergreen.sh b/scripts/install-wintergreen.sh new file mode 100644 index 0000000..34681ef --- /dev/null +++ b/scripts/install-wintergreen.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +. scripts/plugin.sh + +echo "... installing Wintergreen theme" +install wintergreen +echo ".// done" diff --git a/scripts/plugin.sh b/scripts/plugin.sh new file mode 100644 index 0000000..bd6d2c8 --- /dev/null +++ b/scripts/plugin.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +pluginInstallPath="$HOME/.local/lib/nasin/plugins" + +mkdir -p build +mkdir -p "$pluginInstallPath" + +install() { + go build -buildmode=plugin -o "build/$1.so" "./plugins/$1" && \ + cp build/x.so $pluginInstallPath +}